Sie sind auf Seite 1von 554

The Epitaph Survival Guide

Michael James Heron (drakkos@imaginary-realities.com) Blight Draft

Epitaph Online

http://drakkos.co.uk

The Dead Speak


Copyright Notice
All rights, including copyright, in the content of these documents are owned or controlled by the indicated author. You are permited to use this material for your own personal, non-commercial use. This material may be used, adapted, modified, and distributed by the administration of Epitaph Online (http://drakkos.co.uk try the veal) as necessary. You are not otherwise permitted to copy, distribute, download, transmit, show in public, adapt or change in any way the content of this document for any purpose whatsoever without the prior written permission of the indicated author(s). If you wish to use this material for non-personal use, please contact the authors of the texts for permission. If you find these texts useful and want to give less niche programming languages a try, come check out http://www.monkeys-at-keyboards.com for more free instructional material. My apologies for the unfriendly legal boilerplate, but I have had people attempt to steal ownership of my material before. Please direct any comments about this material to drakkos@imaginaryrealities.com.

LPC for Dummies


Those of you who have encountered the Discworld texts known as LPC for Dummies 1 and 2, Working with Others, and Being a Better Creator may find much that is similar in this document. The reason for that is simple its the same material, just updated, revised, expanded, and converted over to the Epitaph Mudlib which is where the original author now resides upon a dark, infernal throne constructed from the skulls of children. If you are using the Epitaph mudlib, this material will be of limited used to you. If you are using the Discworld mudlib, then the Epitaph material will likewise fail to satisfy. As a result of this conversion being done by fallible human hands, there may be rogue Discworld references littering this document like corpses in a river. If you see one of these that isnt justified by context, please send me an email so I can fish it out and give it a decent burial. Thank you!

Epitaph Online

http://drakkos.co.uk

Table of Contents
WELCOME TO THE CREATORBASE ..............................................................................................................................12 YOUVE MADE IT TO THE SAFEHOUSE..................................................................................................................................... 13
To Whom It May Concern: .....................................................................................................................................................13 Being a Creator ......................................................................................................................................................................13 Introduction to Epitaph Creator Culture ................................................................................................................................15 Domain Hierarchy ..................................................................................................................................................................18 Invisibility and You .................................................................................................................................................................19 Your Creating Environment ....................................................................................................................................................20 Creator Commands ................................................................................................................................................................23 Alts and You ...........................................................................................................................................................................28 Epitaph Development Cycle ...................................................................................................................................................29 Conclusion .............................................................................................................................................................................30

INTRODUCTORY LPC ..................................................................................................................................................32 INTRODUCTORY LPC WITH THE EPITAPH LIB ............................................................................................................................ 33
Introduction ...........................................................................................................................................................................33 The Learning Scenario............................................................................................................................................................33 The Structure of the Game ....................................................................................................................................................34 Programming as a Concept ....................................................................................................................................................35 It's All Down To You................................................................................................................................................................38 Conclusion .............................................................................................................................................................................38

MY FIRST ROOM............................................................................................................................................................... 39
Introduction ...........................................................................................................................................................................39 Your Own Deadville ................................................................................................................................................................39 The Basic Template ................................................................................................................................................................40 My Room Sucks ......................................................................................................................................................................42 It's a little too quiet... .............................................................................................................................................................43 A Second Room ......................................................................................................................................................................43 Our Overall Plan .....................................................................................................................................................................44 Property Ladder .....................................................................................................................................................................45 Conclusion .............................................................................................................................................................................46

MY FIRST AREA ................................................................................................................................................................ 47


Introduction ...........................................................................................................................................................................47 The Structure .........................................................................................................................................................................47 Exits and Maintainability........................................................................................................................................................49 Our Exitses .............................................................................................................................................................................51 Chain, chain, chain... ..............................................................................................................................................................52 What is all that crazy STD_OUTSIDE stuff, anyway? ...............................................................................................................54 Conclusion .............................................................................................................................................................................55

BUILDING THE PERFECT BEAST ............................................................................................................................................. 56


Introduction ...........................................................................................................................................................................56 A First NPC .............................................................................................................................................................................56 Breathing Life into the Lifeless ...............................................................................................................................................58 Cover Yourself Up, You'll Catch a Cold ...................................................................................................................................60 Request Item ..........................................................................................................................................................................61 Challenges ..............................................................................................................................................................................61 Chatting Away ........................................................................................................................................................................62 Conclusion .............................................................................................................................................................................65

HOOKING UP ................................................................................................................................................................... 66
Introduction ...........................................................................................................................................................................66 The Path.h Problem ...............................................................................................................................................................66 Sorted! ...................................................................................................................................................................................68 Reset Your Phasers for Stun ...................................................................................................................................................68 If at First You Don't Succeed ..................................................................................................................................................70 Compound Interest ................................................................................................................................................................73 Conclusion .............................................................................................................................................................................75

BACK TO THE BEGINNING .................................................................................................................................................... 76


Introduction ...........................................................................................................................................................................76

Epitaph Online

http://drakkos.co.uk

Captain Beefy's Return...........................................................................................................................................................76 The Road Less Travelled .........................................................................................................................................................77 Bigger, Better, Faster ..............................................................................................................................................................78 Search Profiles .......................................................................................................................................................................79 The Taskmapper .....................................................................................................................................................................80 Task Mapping .........................................................................................................................................................................81 Switching Things Around .......................................................................................................................................................84 Scoping Things Out ................................................................................................................................................................85 Conclusion .............................................................................................................................................................................87

NOW THAT WE'RE AN ITEM ................................................................................................................................................ 88


Introduction ...........................................................................................................................................................................88 Virtually Self-Explanatory.......................................................................................................................................................88 Beefy's Boots .........................................................................................................................................................................90 Bling .......................................................................................................................................................................................91 Item Normalisation ................................................................................................................................................................92 Long Dollars ...........................................................................................................................................................................93 More Item Additions ..............................................................................................................................................................93 Conclusion .............................................................................................................................................................................95

AN INSIDE JOB.................................................................................................................................................................. 96
Introduction ...........................................................................................................................................................................96 The Mysterious Room ............................................................................................................................................................96 Modifying Exits ......................................................................................................................................................................97 Shop 'Till You Drop .................................................................................................................................................................99 Stabby Joe ............................................................................................................................................................................100 Conclusion ...........................................................................................................................................................................101

DYSFUNCTIONAL BEHAVIOUR ............................................................................................................................................ 102


Introduction .........................................................................................................................................................................102 Our Task ...............................................................................................................................................................................102 The Science Bit... Concentrate! ............................................................................................................................................102 The Structure of a Function .................................................................................................................................................103 A Little Bit More... ................................................................................................................................................................105 Function Scope ....................................................................................................................................................................106 Onwards and Upwards! .......................................................................................................................................................106 Violence Begets Violence .....................................................................................................................................................109 Conclusion ...........................................................................................................................................................................112

GOING LOOPY ................................................................................................................................................................ 113


Introduction .........................................................................................................................................................................113 Stabby's Shop .......................................................................................................................................................................113 Long Hand ............................................................................................................................................................................114 Loops ...................................................................................................................................................................................115 Doing While... ......................................................................................................................................................................116 For Loops .............................................................................................................................................................................117 Conclusion ...........................................................................................................................................................................118

ARRAYS, YOU SAY? .......................................................................................................................................................... 119


Introduction .........................................................................................................................................................................119 Slicey Pete ............................................................................................................................................................................119 The Array .............................................................................................................................................................................121 Array Indexing ......................................................................................................................................................................123 Array management ..............................................................................................................................................................123 Fine Dining ...........................................................................................................................................................................124 The Foreach Structure .........................................................................................................................................................126 Conclusion ...........................................................................................................................................................................127

HOORAY FOR ARRAYS ....................................................................................................................................................... 128


Introduction .........................................................................................................................................................................128 A Secret to be Discovered ....................................................................................................................................................128 A Grue Some Fate! ...............................................................................................................................................................129 Getting To Our Shop of Horror .............................................................................................................................................133 Tell Events ............................................................................................................................................................................134 Conclusion ...........................................................................................................................................................................135

MAPPING IT OUT ............................................................................................................................................................ 136


Introduction .........................................................................................................................................................................136 The Mapping ........................................................................................................................................................................136 The Magic Hate Ball .............................................................................................................................................................138 More Mapping Manipulation, Matey ..................................................................................................................................141 Conclusion ...........................................................................................................................................................................144

Epitaph Online

http://drakkos.co.uk

DEBUGGING ................................................................................................................................................................... 145


Introduction .........................................................................................................................................................................145 The Two Different Kinds of Errors ........................................................................................................................................145 Fixing your Syntax Errors ......................................................................................................................................................145 Logic Errors ..........................................................................................................................................................................148 I found an error! ..................................................................................................................................................................149 Conclusion ...........................................................................................................................................................................151

SO LONG ....................................................................................................................................................................... 153


Introduction .........................................................................................................................................................................153 The Past ...............................................................................................................................................................................153 The Present ..........................................................................................................................................................................155 The Future ...........................................................................................................................................................................155 Conclusion ...........................................................................................................................................................................156

READER EXERCISES .......................................................................................................................................................... 157


Introduction .........................................................................................................................................................................157 Exercises ..............................................................................................................................................................................157 Send Suggestions .................................................................................................................................................................159

BEING A BETTER CREATOR .......................................................................................................................................160 WELCOME TO THE LEARNING ZONE .................................................................................................................................... 161
Introduction .........................................................................................................................................................................161 The Plan for This Course ......................................................................................................................................................161 What's In A Game? ..............................................................................................................................................................162 Design Philosophy of Areas ..................................................................................................................................................164 Zombieville ..........................................................................................................................................................................165 Conclusion ...........................................................................................................................................................................166

PLAYER DEMOGRAPHICS ................................................................................................................................................... 167


Introduction .........................................................................................................................................................................167 Bartle's Definitions...............................................................................................................................................................167 Your Zone's Players ..............................................................................................................................................................168 Catering For Achievers .........................................................................................................................................................168 Catering for Explorers ..........................................................................................................................................................170 Catering for Socialisers.........................................................................................................................................................171 Catering for Imposers ..........................................................................................................................................................172 Conclusion ...........................................................................................................................................................................172

URBAN PLANNING........................................................................................................................................................... 173


Introduction .........................................................................................................................................................................173 An Interesting Area ..............................................................................................................................................................173 The Refined Feature Density Model ....................................................................................................................................174 Feature Planning ..................................................................................................................................................................177 Conclusion ...........................................................................................................................................................................179

THEMATIC CONSIDERATIONS ............................................................................................................................................. 180


Introduction .........................................................................................................................................................................180 Starting Principles ................................................................................................................................................................180 The Opportunities ................................................................................................................................................................181 Profanity and Racism ...........................................................................................................................................................182 Humour ................................................................................................................................................................................183 Player Input ..........................................................................................................................................................................184 Back to Zombieville ..............................................................................................................................................................185 Conclusion ...........................................................................................................................................................................187

THE BEST LAID SCHEMES .................................................................................................................................................. 188


Introduction .........................................................................................................................................................................188 Making the Map...................................................................................................................................................................188 Incorporating Features.........................................................................................................................................................189 The School ...........................................................................................................................................................................191 Instances ..............................................................................................................................................................................191 The Shop ..............................................................................................................................................................................192 It All Adds Up .......................................................................................................................................................................192 Conclusion ...........................................................................................................................................................................193

QUEST DESIGN ............................................................................................................................................................... 194


Introduction .........................................................................................................................................................................194 Why Quests? ........................................................................................................................................................................194 Hand-Crafted Craziness ........................................................................................................................................................195 The Anatomy of a Epitaph Quest .........................................................................................................................................196

Epitaph Online

http://drakkos.co.uk

Conclusion ...........................................................................................................................................................................201

LET'S TALK ABOUT QUESTS, BABY ....................................................................................................................................... 202


Introduction .........................................................................................................................................................................202 The Intention of a Quest ......................................................................................................................................................202 The Zombieville Quests ........................................................................................................................................................203 Conclusion ...........................................................................................................................................................................205

DYNAMIC QUEST DESIGN ................................................................................................................................................. 206


Introduction .........................................................................................................................................................................206 Dynamic and Linear Quests .................................................................................................................................................206 Which are Best? ...................................................................................................................................................................207 Some Guiding Principles For Dynamic Quest Design ...........................................................................................................208 Dynamic Quests The Book Sorting Quest ..........................................................................................................................210 Dynamic Quests The Partition Quest ................................................................................................................................212 Dynamic Quests The Secret Room Quest ..........................................................................................................................213 Conclusion ...........................................................................................................................................................................213

THE TEN COMMANDMENTS OF ROOM DESCRIPTIONS ............................................................................................................ 215


Introduction .........................................................................................................................................................................215 The Ten Commandments .....................................................................................................................................................215 Conclusion ...........................................................................................................................................................................222

THE ART OF WRITING ROOM DESCRIPTIONS ........................................................................................................................ 223


Introduction .........................................................................................................................................................................223 Your Mental Picture .............................................................................................................................................................223 Hand-Crafting versus Procedural Descriptions ....................................................................................................................226 Room Chats ..........................................................................................................................................................................228 References ...........................................................................................................................................................................229 Quality Assurance ................................................................................................................................................................230 Conclusion ...........................................................................................................................................................................231

NON PLAYER CHARACTERS ................................................................................................................................................ 232


Introduction .........................................................................................................................................................................232 NPC Classifications ...............................................................................................................................................................232 NPC Descriptions .................................................................................................................................................................234 Equipment ...........................................................................................................................................................................235 Dialog ...................................................................................................................................................................................236 Conclusion ...........................................................................................................................................................................238

BEASTS OF ZOMBIEVILLE ................................................................................................................................................... 239


Introduction .........................................................................................................................................................................239 Our NPC Manifesto ..............................................................................................................................................................239 The Zombies.........................................................................................................................................................................240 The Survivor .........................................................................................................................................................................241 The Headmistress ................................................................................................................................................................243 Boss Battles ..........................................................................................................................................................................244 Conclusion ...........................................................................................................................................................................246

FEATURE DEVELOPMENT................................................................................................................................................... 247


Introduction .........................................................................................................................................................................247 A Local Shop For Local People ..............................................................................................................................................247 The Library ...........................................................................................................................................................................248 Feature Creep ......................................................................................................................................................................250 Conclusion ...........................................................................................................................................................................252

FINISHING UP................................................................................................................................................................. 253


Introduction .........................................................................................................................................................................253 Integration Mechanisms ......................................................................................................................................................253 Advertisement .....................................................................................................................................................................254 Bookkeeping ........................................................................................................................................................................255 Lessons Learned...................................................................................................................................................................257 Conclusion ...........................................................................................................................................................................257

LESSONS LEARNED........................................................................................................................................................... 259


Introduction .........................................................................................................................................................................259 Death March Projects ..........................................................................................................................................................259 Realism over Fun..................................................................................................................................................................260 Randomness Is Not Challenging ...........................................................................................................................................261 Complexity is not King .........................................................................................................................................................261 Complaints Are Not Representative .....................................................................................................................................262 Know When to Step Back .....................................................................................................................................................262 For God's Sake, Have Fun .....................................................................................................................................................263

Epitaph Online

http://drakkos.co.uk

Conclusion ...........................................................................................................................................................................264

FINAL THOUGHTS ............................................................................................................................................................ 265


Introduction .........................................................................................................................................................................265 What Happens Now? ...........................................................................................................................................................265 Keeping Our Players Happy ..................................................................................................................................................265 Conclusion ...........................................................................................................................................................................266

INTERMEDIATE LPC ..................................................................................................................................................267 THE MUDLIB STRIKES BACK............................................................................................................................................... 268


Introduction .........................................................................................................................................................................268 Zombieville ..........................................................................................................................................................................268 The Village Plan....................................................................................................................................................................269 A Word Of Caution ...............................................................................................................................................................269 Conclusion ...........................................................................................................................................................................270

INHERITANCE .................................................................................................................................................................. 271


Introduction .........................................................................................................................................................................271 Inheritance ...........................................................................................................................................................................271 Hooking It All Up ..................................................................................................................................................................272 Replace Program ..................................................................................................................................................................273 Success! ...............................................................................................................................................................................274 However... ............................................................................................................................................................................275 Multiple Inheritance and Scope Resolution .........................................................................................................................276 Our Zombieville Room Inherit ..............................................................................................................................................278 Visibility ...............................................................................................................................................................................279 The Impact of Change ..........................................................................................................................................................281 Conclusion ...........................................................................................................................................................................281

THE LIBRARY QUEST ........................................................................................................................................................ 283


Introduction .........................................................................................................................................................................283 An Epitaph Quest .................................................................................................................................................................283 Data Representation ............................................................................................................................................................284 In A Class of Your Own .........................................................................................................................................................287 The Library State ..................................................................................................................................................................290 Dynamic Quest Design .........................................................................................................................................................290 The Rest of the Quest ..........................................................................................................................................................291 Conclusion ...........................................................................................................................................................................292

ADDING COMMANDS....................................................................................................................................................... 293


Introduction .........................................................................................................................................................................293 Deciding On a User Interface ...............................................................................................................................................293 Adding Commands ...............................................................................................................................................................295 Failure Messages..................................................................................................................................................................297 More on Patterns .................................................................................................................................................................299 Optional Parameters and Set Choices ..................................................................................................................................301 Direct and Indirect Objects ..................................................................................................................................................301 Passed Parameters ...............................................................................................................................................................302 Tannah's Pattern Matcher ....................................................................................................................................................303 Conclusion ...........................................................................................................................................................................304

THE LIBRARY ROOM ........................................................................................................................................................ 305


Introduction .........................................................................................................................................................................305 The Room .............................................................................................................................................................................305 Guess the Syntax Quests ......................................................................................................................................................309 Viewing the Blurb ................................................................................................................................................................310 The Sort Command ..............................................................................................................................................................311 Except, not quite... ...............................................................................................................................................................314 Conclusion ...........................................................................................................................................................................316

QUEST HANDLERS ........................................................................................................................................................... 317


Introduction .........................................................................................................................................................................317 The Handlers ........................................................................................................................................................................317 Cast Your Mind Back... .........................................................................................................................................................318 Our Second Quest ................................................................................................................................................................319 Conclusion ...........................................................................................................................................................................322

OUR LAST QUEST ............................................................................................................................................................ 323


Introduction .........................................................................................................................................................................323 The Quest Design .................................................................................................................................................................323 Handlers ...............................................................................................................................................................................324

Epitaph Online

http://drakkos.co.uk

The Portrait Handler ............................................................................................................................................................325 Back to the Library ...............................................................................................................................................................330 Conclusion ...........................................................................................................................................................................331

FINISHING TOUCHES ........................................................................................................................................................ 333


Introduction .........................................................................................................................................................................333 The Second Level Room .......................................................................................................................................................333 I'm Looking Through... uh... behind you ..............................................................................................................................334 Ecretsay Odecay ...................................................................................................................................................................335 Random Guessing ................................................................................................................................................................336 What Kind Of Day Has It Been? ............................................................................................................................................337 Conclusion ...........................................................................................................................................................................338

ZOMBIES ....................................................................................................................................................................... 339


Introduction .........................................................................................................................................................................339 The Zombie Template ..........................................................................................................................................................339 Event Driven Programming ..................................................................................................................................................343 The Power of Inherits...........................................................................................................................................................345 Conclusion ...........................................................................................................................................................................346

AGNES MCKAY ............................................................................................................................................................... 347


Introduction .........................................................................................................................................................................347 Agnes ...................................................................................................................................................................................347 Librarian Notes and Callbacks ..............................................................................................................................................348 Back to the Portrait Handler ................................................................................................................................................349 Data Persistence ..................................................................................................................................................................352 Effective User Identifiers ......................................................................................................................................................352 Conclusion ...........................................................................................................................................................................355

FUNCTION POINTERS ....................................................................................................................................................... 356


Introduction .........................................................................................................................................................................356 The Structure of a Function Pointer .....................................................................................................................................356 The Four Holy Efuns .............................................................................................................................................................358 Back to the Library ...............................................................................................................................................................360 Function Pointer Support .....................................................................................................................................................362 A Few More Things about Function Pointers .......................................................................................................................363 Conclusion ...........................................................................................................................................................................364

THE HEADMISTRESS......................................................................................................................................................... 365


Introduction .........................................................................................................................................................................365 The Skeleton of a Boss Zombie ............................................................................................................................................365 NPC Combat Behaviours ......................................................................................................................................................368 Boss Powers .........................................................................................................................................................................370 Pus Attack ............................................................................................................................................................................371 Boss Drops ...........................................................................................................................................................................372 Conclusion ...........................................................................................................................................................................373

SHOPPING AROUND ........................................................................................................................................................ 374


Introduction .........................................................................................................................................................................374 Item Development ...............................................................................................................................................................374 Auto Loading ........................................................................................................................................................................375 Dressing Up ..........................................................................................................................................................................376 Back To The Auto Loading ....................................................................................................................................................377 Doing It Better .....................................................................................................................................................................379 Back to our Shop ..................................................................................................................................................................379 Conclusion ...........................................................................................................................................................................380

CAUSE AND EFFECT ......................................................................................................................................................... 381


Introduction .........................................................................................................................................................................381 What's An Effect? .................................................................................................................................................................381 The Power of Effects ............................................................................................................................................................384 Working with Effects ............................................................................................................................................................387 Bits and Pieces .....................................................................................................................................................................389 Conclusion ...........................................................................................................................................................................390

ACHIEVING YOUR POTENTIAL............................................................................................................................................. 391


Introduction .........................................................................................................................................................................391 Achievements and Quests ...................................................................................................................................................391 How Often Should I Add Achievements? .............................................................................................................................392 My First Achievement ..........................................................................................................................................................393 Criteria .................................................................................................................................................................................394 Criteria Matching .................................................................................................................................................................395 A Little More Complicated ...................................................................................................................................................397

Epitaph Online

http://drakkos.co.uk

Other Achievement Settings ................................................................................................................................................400 I've written an achievement! What now? ............................................................................................................................400 Ack, it sucks! How do I get rid of it? .....................................................................................................................................401 Achievement Levels .............................................................................................................................................................401 Conclusion ...........................................................................................................................................................................402

SO HERE WE ARE AGAIN.................................................................................................................................................. 403


Introduction .........................................................................................................................................................................403 What's On Your Mind? .........................................................................................................................................................403 Help Us to Help You .............................................................................................................................................................404 Where Do You Go From Here? .............................................................................................................................................404 Conclusion ...........................................................................................................................................................................405

WORKING WITH OTHERS .........................................................................................................................................406 PEOPLE SKILLS ................................................................................................................................................................ 407


Introduction .........................................................................................................................................................................407 Whole New Skills .................................................................................................................................................................407 Standard Standards ..............................................................................................................................................................408 Professionalism ....................................................................................................................................................................409 Conclusion ...........................................................................................................................................................................410

CODE LAYOUT ................................................................................................................................................................ 411


Introduction .........................................................................................................................................................................411 Code Formatting ..................................................................................................................................................................411 Conclusion ...........................................................................................................................................................................416

COLLABORATION ............................................................................................................................................................. 417


Introduction .........................................................................................................................................................................417 The Social Context of Collaboration .....................................................................................................................................417 Development in Volunteer Environments ............................................................................................................................418 What Are The Benefits of Collaboration? ............................................................................................................................419 Collaboration Tools on Epitaph ............................................................................................................................................420 A Suggested Collaboration Process ......................................................................................................................................421 Conclusion ...........................................................................................................................................................................422

SOCIAL CAPITAL .............................................................................................................................................................. 423


Introduction .........................................................................................................................................................................423 Creator Politics and MUDs ...................................................................................................................................................423 The Ten Commandments Of Egoless Programming .............................................................................................................424 Trust and Common Ground .................................................................................................................................................426 The Trust Triad .....................................................................................................................................................................428 Conclusion ...........................................................................................................................................................................429

THE DARK ART OF REFACTORING........................................................................................................................................ 431


Introduction .........................................................................................................................................................................431 Refactoring...........................................................................................................................................................................431 Good Code ...........................................................................................................................................................................431 Impact of Change .................................................................................................................................................................431 The Rules .............................................................................................................................................................................432 Breaking the Rules ...............................................................................................................................................................434 Refactoring...........................................................................................................................................................................435 Some Common Refactoring Tasks ........................................................................................................................................436 Conclusion ...........................................................................................................................................................................436

CODING ETIQUITTE.......................................................................................................................................................... 437


Introduction .........................................................................................................................................................................437 Before You Write Any Code ..................................................................................................................................................437 When You Are Writing Code ................................................................................................................................................438 When You Have Written Code .............................................................................................................................................441 Conclusion ...........................................................................................................................................................................442

SOURCE CONTROL ........................................................................................................................................................... 443


Introduction .........................................................................................................................................................................443 Source Control in the Abstract .............................................................................................................................................443 The Epitaph RCS System .......................................................................................................................................................444 Problems ..............................................................................................................................................................................448 Conclusion ...........................................................................................................................................................................449

DOCUMENTATION ........................................................................................................................................................... 450


Introduction .........................................................................................................................................................................450 Commenting ........................................................................................................................................................................450 Commenting Good Practice .................................................................................................................................................451

Epitaph Online

http://drakkos.co.uk

Autodoc ...............................................................................................................................................................................452 The Autodoc Process ...........................................................................................................................................................455 Other Help-Files ...................................................................................................................................................................456 Why Document? ..................................................................................................................................................................457 Conclusion ...........................................................................................................................................................................457

DOMAIN INTEGRATION ..................................................................................................................................................... 458


Introduction .........................................................................................................................................................................458 Multiple Developers the Traditional Approach .................................................................................................................458 Examples of this on Epitaph .................................................................................................................................................459 Continuous Integration ........................................................................................................................................................460 A Framework for Area Integration .......................................................................................................................................461 Conclusion ...........................................................................................................................................................................462

GROUP DYNAMICS .......................................................................................................................................................... 464


Introduction .........................................................................................................................................................................464 What is a Domain? ...............................................................................................................................................................464 When Is A Group Not A Group? ...........................................................................................................................................466 Group Roles .........................................................................................................................................................................467 Group-think .........................................................................................................................................................................469 Conclusion ...........................................................................................................................................................................470

PROJECT MANAGEMENT .................................................................................................................................................. 471


Introduction .........................................................................................................................................................................471 Project Management 101 ....................................................................................................................................................471 Frameworks .........................................................................................................................................................................472 Communication and Team Roles ..........................................................................................................................................473 Subdivision of Effort and Ownership....................................................................................................................................474 The Epitaph Project Tracker .................................................................................................................................................475 Conclusion ...........................................................................................................................................................................479

MAINTENANCE ............................................................................................................................................................... 480


Introduction .........................................................................................................................................................................480 Maintenance in the Software Development Process ...........................................................................................................480 Domain Maintenance ..........................................................................................................................................................481 Where Do Bugs Come From? ...............................................................................................................................................482 Bug Triage ............................................................................................................................................................................483 The Error Handler ................................................................................................................................................................484 Conclusion ...........................................................................................................................................................................487

THE EXPERIENCE DIVIDE ................................................................................................................................................... 488


Introduction .........................................................................................................................................................................488 Professional and Amateur Programmers .............................................................................................................................488 Deep Smarts ........................................................................................................................................................................490 The Tension ..........................................................................................................................................................................491 Strategies for Success ..........................................................................................................................................................492 Conclusion ...........................................................................................................................................................................493

WRAPPING UP ............................................................................................................................................................... 495


Introduction .........................................................................................................................................................................495 Collegiality ...........................................................................................................................................................................495 Conclusion ...........................................................................................................................................................................495

EPITAPH QUICK REFERENCES ...................................................................................................................................497 QUICK REFERENCE - RUMOUR OBJECTS ............................................................................................................................... 498
Introduction .........................................................................................................................................................................498 Why Do We Use Them? .......................................................................................................................................................498 When to add a rumour object .............................................................................................................................................498 What do they look like? .......................................................................................................................................................498

QUICK REFERENCE - POWERED ITEMS ................................................................................................................................. 500


Introduction .........................................................................................................................................................................500 Why Do We Use Them? .......................................................................................................................................................500 When to add a powered item ..............................................................................................................................................500 What do they look like? .......................................................................................................................................................500

QUICK REFERENCE - CITY STREET ....................................................................................................................................... 504


Introduction .........................................................................................................................................................................504 What is a City Street ............................................................................................................................................................504 Why Do We Use Them? .......................................................................................................................................................504 What Do They Look Like? .....................................................................................................................................................505

QUICK REFERENCE - TEMPLATED ROOMS ............................................................................................................................. 508

10

Epitaph Online

http://drakkos.co.uk

Introduction .........................................................................................................................................................................508 What is a Template ..............................................................................................................................................................508 Why Do We Use Them? .......................................................................................................................................................508 What Do They Look Like.......................................................................................................................................................508

QUICK REFERENCE - CITY LANDMARK .................................................................................................................................. 511


Introduction .........................................................................................................................................................................511 What is a Landmark .............................................................................................................................................................511 Why Do We Use Them? .......................................................................................................................................................511 When to add a landmark .....................................................................................................................................................511 Getting your Landmark to Show Up .....................................................................................................................................511 Landmarks and the City .......................................................................................................................................................512 What does the data file look like?........................................................................................................................................512 Items and Shorts ..................................................................................................................................................................515 Aliases and Descriptions ......................................................................................................................................................515 Other Add Items ..................................................................................................................................................................516 The Rest ...............................................................................................................................................................................518 Plotted Landmarks ...............................................................................................................................................................518

QUICK REFERENCE - A PRACTISE OBJECT ............................................................................................................................. 520


Introduction .........................................................................................................................................................................520 What is a Practise Object .....................................................................................................................................................520 Why Do We Use Them? .......................................................................................................................................................520 When to add a practise object .............................................................................................................................................520 What do they look like .........................................................................................................................................................520 Textbooks .............................................................................................................................................................................522 Object Levels ........................................................................................................................................................................523 Powered Practise Objects ....................................................................................................................................................523

QUICK REFERENCE - A MAESTRO MODULE .......................................................................................................................... 526


Introduction .........................................................................................................................................................................526 What is a Maestro Module ..................................................................................................................................................526 Why Do We Use Them? .......................................................................................................................................................526 When to add a Maestro Module ..........................................................................................................................................526 What Does A Maestro Module Look Like .............................................................................................................................526 Generating the Event ...........................................................................................................................................................528

QUICK REFERENCE - FACTIONS ........................................................................................................................................... 529


Introduction .........................................................................................................................................................................529 What is a Faction .................................................................................................................................................................529 Why Do We Use Them? .......................................................................................................................................................529 When to add a Faction .........................................................................................................................................................529 An Example Faction File .......................................................................................................................................................529 Faction Infrastructure ..........................................................................................................................................................532

QUICK REFERENCE - CRAFTING .......................................................................................................................................... 534


Introduction .........................................................................................................................................................................534 What is crafting ....................................................................................................................................................................534 Why Do We Use Them? .......................................................................................................................................................534 When to add a Crafting Pattern ...........................................................................................................................................534 A Simple Crafting Pattern .....................................................................................................................................................534 A Slightly More Complex Example .......................................................................................................................................537 Finished products ................................................................................................................................................................539

APPENDIX ................................................................................................................................................................541 APPENDIX 1: CODE OWNERSHIP POLICY .............................................................................................................................. 542 Introduction ........................................................................................................................................................... 542 Code Ownership .................................................................................................................................................... 542 When Does It Become Epitaph Code? ................................................................................................................... 542 Epitaph and Discworld........................................................................................................................................... 543 Shared Ownership ................................................................................................................................................. 543 Profiting ................................................................................................................................................................. 544 Not a Straightjacket .............................................................................................................................................. 544 APPENDIX 2 - FEATURE DENSITY ........................................................................................................................................ 546 APPENDIX 3 - GLOSSARY................................................................................................................................................... 548 APPENDIX 4 - FURTHER READING LIST ................................................................................................................................. 550
Game Design ........................................................................................................................................................................550 Theme and Writing ..............................................................................................................................................................551

11

Epitaph Online

http://drakkos.co.uk

Social Contexts .....................................................................................................................................................................552 Miscellaneous ......................................................................................................................................................................552

INDEX ......................................................................................................................................................................553

Section Zero

Welcome to the Creatorbase

12

Epitaph Online

http://drakkos.co.uk

Youve made it to the Safehouse


To Whom It May Concern:
The manual you have in your hands is a tome of great power - it details the ways in which you as an Epitaph Developer can shape the world in which you are functioning. You have either been selected to be a creator by the administration of an Epitaph based MUD, or you are the administrator of your own MUD using the Epitaph lib. If you're not either of these things, you'll probably find this text rather dry, unsettling, and ultimately of little use. For the rest of this document, well assume you were employed on Epitaph. For everyone else though, some words of comfort: Its okay to be nervous about this its a big step, and youve signed up for a lot of extra responsibility. All we ask is that you do the best you can there are people in the Support domain who are there to help ease this transition and provide you with the guidance you need to become the Top Notch Creator that your employer believes you can be!

Being a Creator
The fact that youve actually been promoted shows a number of things. The first is that when given a particular task (as undoubtedly happened as part of your creator application) you showed that you could knuckle down and meet the request. Thats may not sound like anything, but youd probably be surprised at the number of people who give up at that first hurdle. Being a creator, regardless of what you may have heard, is not easy. On older MUDs, it was traditional for players who reached a certain level to be instantly promoted to the rank of God. Thats not the case here the game isnt over for you, youve just added an entirely new facet to it. However, that new facet comes with responsibilities. First and foremost of these responsibilities is your own willingness to learn. Although there are people there to help provide you with guidance, we cant actively teach you its just not possible when everyone is spread over many different countries, and time zones. As such, you have to be proactive in learning how to code ultimately the person who is responsible for your eventual success is you. You wouldnt be here though if someone didnt believe that you had that in you much as youd be surprised at the number of people who never complete their application tasks, youd probably also be surprised at the number of people who are rejected as creators. There are certain criteria that an individual is expected to fulfil, and while these vary from lord to lord, they tend to fall into a number of broad categories:

13

Epitaph Online

http://drakkos.co.uk

You are expected to be a model player. While some brushes with the law can be forgiven, especially if they happened in the distant past or were relatively minor, on the whole you should have a clean player record. You are expected to be active. While real life will always get in the way of your volunteer efforts here, and while real life should always be prioritized over what you do as an Epitaph creator, in the end you are expected to produce some content for the MUD on a regular basis. You are expected to have an appreciation of both the structure of the MUD, and the thematic constraints of the setting. You need to have played for a while, and you need to be familiar with our game canon.

As a creator, you have considerable power over very large swathes of the MUD. We have to trust you to use that power responsibly. Some people cant handle the temptation, and try to turn their creator powers to advantage their own player characters, or those of their friends. Do not break this rule, or you are going to get caught. There is always someone who thinks theyre smarter than the people running the game. Seriously, youre not... there are always people watching. As a creator, you are apart from the game you cannot do anything that lends an in-game advantage to any other player. This rule is strict, and it is inviolable. If you break it, youll be dismissed, and youll never get another chance to be a creator. While we do allow second chances, we dont forgive this particular infraction. You may find yourself under considerable pressure from your friends to demonstrate your new cosmic powers through gifts of skills, items and cash. Dont give in to this temptation you worked quite hard to get this gig, you dont want to throw it away. Being a creator, or at least being a coding creator, is not about policing the MUD. There are creators who have that specific responsibility, but its not yours. Your job is to develop the game that people are playing and enjoying. The people who are responsible for policing the game are certain members of the Support domain, and those members of Admin. If you feel that something needs to be addressed, you should either contact a member of the Admin domain directly, or make a post about it on the bulletin boards. Only in the most extreme of circumstances should you intervene directly. As you progress in your role as a Epitaph creator, it will become obvious to you when such situations arise if youre not sure, err on the side of caution. As a creator, you gain the ability to switch between visibility (everyone can see you) and invisibility (only fellow creators and those players to whom you have granted permission can see you). As a new creator, you should instantly go invisible and stay that way. The command to do this is simply invis. Well talk more about being invisible later. Players will often pose questions to visible creators some of these questions will be quite innocent, and some will be requests for assistance. You do not yet know enough about how things are done to render useful assistance, so remove yourself from that situation by going invisible. If you are asked for help by people on your allow list, either direct them to an online liaison or director, or ask them to wait if none are online. 14

Epitaph Online

http://drakkos.co.uk

Being a creator also carries with it a very heavy security obligation you are never to reveal creator information to players, or to allow yourself to profit directly from your knowledge of creator information. This restriction is in place to protect you from accusations of impropriety, and also to protect the integrity of the game the hidden values of commands, skills, and items are hidden for a reason we dont want players to know them. You must not confirm or deny anything you are asked, or provide any answers that a player cannot provide. Only if you have been authorized to reveal information by your domain leader can you do so. Any violation of this will carry with it strict penalties, up to and including dismissal. Your obligation on Epitaph is to contribute each domain leader will have their own benchmarks for what is considered to be an acceptable level of activity, but on the whole it can be summed up as you are expected to make a measurable contribution to your domain. Your domain leader, and any domain deputies, will check up on you on a regular basis to ensure youre managing, but you should be proactive in getting touch if you feel you are not. It may sound like an awful lot of work to be a creator. Thats because it is! But its also great fun and youll get to create whole worlds using nothing but your mind and your computer. Along the way, youll learn genuinely useful life skills there are any number of creators who can trace real life employment opportunities to what they learned as creators here.

Introduction to Epitaph Creator Culture


Its a whole new world on this side of the fence, and that is going to show. Its an alien culture, and unless youve had a lot of exposure to the way creators think and talk, youre going to find it somewhat intimidating. The general rule you should be following here is one of cautious integration. Thats not because were an unfriendly bunch... I mean, we are, but thats not why Im suggesting this. The creator-base is like any online community it has its own culture, its own language, its own in-jokes and its own etiquette. You, as a new creator, dont know any of these. Your first exposure to creator culture is more than likely to be the creator channel. Thats the main communication channel for Epitaph creators, and is a mostly social environment. Its for jokes, gossip, and general chat. You should feel entirely comfortable with chatting on this channel, although bear in mind that for many of the regulars on the channel, youll be an entirely new name. You dont want to be the guy at the party who nobody knows and yet persists on monopolizing every conversation! Most of what you do as a newbie creator is read this document is only one of the things that is on your to read pile. There are also a great many more bulletin boards than you may have remembered. The creator boards you should read regularly vital information is often posted there, and its an easy way to build your knowledge of Epitaph creator culture. The player ones you can choose to follow as you wish, but if you choose not to, you run the risk of losing touch with what the players are saying. 15

Epitaph Online

http://drakkos.co.uk

The boards are accessible as usual through the website, but you can also carry a bulletin board around with you at all times so you can have access in game. The clone command is used to create copies of items to get your very own copy of an in-game bulletin board, you type: clone /items/creator/board_mas Once youve done that, type summary. There are a lot of boards... a lot of boards. Some of these are more important than others. The important boards for now are commonroom, mudlib, and the board that belongs to your own domain. You should read the other creator boards too, but you can put them to the bottom of your pile. Those boards you definitely do not want to read can be removed from your list by using the killfile command: killfile flame As far as your contribution to the player bulletin boards go well, that should stop now. Your words carry additional weight as a creator, and you run a huge risk of being misinterpreted, or having your own personal opinion quoted as Creator opinion. Its not so much of an issue with the social boards such as five minute hate, love-in, debates and so forth but if you post as a creator on a player board, youre asking for trouble. The best advice I can give is to post as a player alt. If you dont have one, create one that way, you avoid the impression that you are speaking from on high. Remember too that you are now one of us, and if you have a concern about the game your first duty is to report it to your colleagues. While were not a mind-cult, and while nobody is demanding that you toe the party line, we dont like to read your objections for the first time coming from your player alt. I personally recommend the following as guidelines for your board usage: Avoid posting to a game-related board as a creator. If you have something to add to an idea being bounced around, then log in a player alt to make your contribution. Some players may see your postings as coming from a position of authority, and unless you're in a position where that's accurate, it should be avoided. If logging in an alt to post is too much effort, it may be an idea to reconsider whether your posting is entirely necessary. If you post as a creator, please don't post about developments with which you are not directly involved. The domain leader and domain deputies of a domain should make the final call as to whether information on a particular development should be publicized. This also goes for posting about games mechanics outside your jurisdiction. If you're not sure if you should be posting something, don't do it. If you post, speak for yourself. Don't speak for all creators, since we all have our own opinions which may, or may not, coincide with what you post. Referencing to the rest of the creator-base is rude, and inconsiderate. 16

Epitaph Online

http://drakkos.co.uk

Really, it all comes down to this: if you're newly promoted, please think twice about posting in the first place. Give yourself time to come to grips with your new duties and responsibilities. It's a natural urge to want to express your opinions, especially from a new position of authority, but that should wait until you're more established as a creator. Log in an alt to post... that way, you don't run the risk of what you say reflecting badly on creators as a whole. These guidelines also apply to your interactions with the normal talker channels. As with the creator channel, its well worth keeping a low(ish) profile for a while. If you have any questions about code, then your first port of call is your domain leader it is their responsibility to ensure that you are properly supported in the work you are doing. No one person can know everything though, so if you find no luck there, you can ask your question on the (code) channel. There is also a special tool called the Oracle. If you have a query that gets resolved, then the chances are someone else would be interested in that knowledge at some point, so it is appreciated when you can record your question and the answer on the Oracle knowledge base. You can also ask a question you havent been able to get resolved previously, and perhaps someone who wasnt online when you had the problem will be able to help. As has been mentioned, the Epitaph creatorbase has a culture all of its own, and that culture is mainly implicit and changes over time. Some of the more important implicit rules to follow will be covered here: It is acceptable to read over the code of in game areas, obviously... this is where you get a lot of inspiration as to how to solve your own coding problems. However, if you wish to look over code in development or playtesting, you should ask the creator in charge of the development if they mind most wont, but its nice to be nice. This also applies to wandering around in PT/development areas... if there are no open invitations to explore the area then you should ask permission. The same also applies to code within /w/ directories... it is considered rude to indiscriminately read, copy or reference to code/files within a creator's personal directory. It is also considered rude to clone objects from their directory without permission. Again, if code/objects are within common circulation within the creatorbase you don't need to worry too much about this, but it is always polite to make sure. Peripherally related to this, please beware when you're playing around with in-game code. It's a natural desire to want to understand the workings of some of the NPCs and rooms you've experienced as a player, but remember in the majority of cases actually interacting with these objects is interfering with something that is in-game. This also applies to cloning unique NPCs into your workroom - rooms that clone unique NPCs assume no more than one instance of the NPC is loaded at any one time. If you have a copy of the NPC in your room, it's possible that you will interfere with the NPC actually being cloned in the game. Likewise, some NPCs have stats that save, or interact in various Arcane Ways with handlers around the game-world. Multiple copies of these NPCs being in existence at any one time is very likely to cause Interesting 17

Epitaph Online

http://drakkos.co.uk

Problems. Unless you know for certain that cloning another copy of the NPC is not going to have negative effects on the smooth running of the MUD, please don't clone them or move them to your workroom. Use the more command to read the code of the loaded version if you want to see how it works. With regards to creator invisibility, it is important you do not pass information regarding this to players. If you are asked by a player if a particular creator is on but invisible, then ask the creator in question if they want to speak to that person. If not, you can tell the player that you are unable to pass on information regarding a creator's invisibility. This goes equally for Lord/High Lord invisibility and general creatordom. If you are on a Lord's allow list and a creator or player asks you if they are online, make sure you ask the Lord if they want to talk to the person. All creators should set a valid e-mail in their finger info. If you don't do this and at some time in the future you, for example, disappear without word on some secret mission, then your domain leader won't be able to mail you and ask what's going on, and will instead have to just demote you. Please make sure your domain leader has a means of getting in touch with you outside of the MUD on those occasions when it is vital they make contact

Domain Hierarchy
As a new creator, you are going to be taking a position in an existing organizational hierarchy. First of all, you belong to a domain, usually either 'game' or 'support'. The 'game' domain is responsible for... well, the game. They create the areas, write the NPCs, and build the quests. More senior creators in the game domain are also responsible for the architecture of the game - commands, and the various code files that drive the rest of the MUD. The support domain has a more ephemeral brief, and covers anything that supports the creators responsible for the development of the game, such as public relations and the creation of documentation such as this. Each domain has a Domain Leader (usually a member of admin), and the Domain Leader is responsible for the hiring, firing, and general direction of their domain. Most domain leaders also appoint a number of Domain Deputies, who have some delegated authority within the domain. The exact amount of authority varies from domain to domain, but it is safe to say that their word carries significant weight with your domain leader, or they wouldnt be deputies at all. The domains are mostly independent, although cross-domain communication at the level of domain leaders helps to co-ordinate development. On the whole though, you are likely to be assigned a project in an existing development as part of an existing plan there will be plenty of scope for creativity, but at least initially your efforts will be directed as to where your domain administration (leader and deputies) feel they are most useful. If you have particular interest in a development, you should let your domain administration know finding your project should be, as far as is possible, a collaborative effort. Domain leaders have broad autonomy, but the domain of Admin contains the last 18

Epitaph Online

http://drakkos.co.uk

word on Epitaph. In this domain may be found the ultimate authority, from which all other authority flows. Within the virtual parameters of Epitaph, their word is Law. While the admin domain has a leader like other domains, you can think of the leader of the admin domain as more of a first amongst equals than the boss. Each member of admin will have specific responsibility in which they are the ultimate arbitrator of the MUDs direction, but most admin work generally across the game also. Running parallel to this domain hierarchy is a system of creator ranks. Your rank will begin at creator we do not distinguish between new creators and existing creators, there is no junior creator rank. Once you have proven yourself, which is at the discretion of your domain leader, you might be promoted to senior creator. Usually this will not happen before you have at least a years contribution, and a substantial project under your belt. Senior creator, as a position, is largely honourific and does not carry with it any particular extra responsibilities, although you do gain a little extra access to the MUD's file system. Next up the ladder is the rank of Lord. Lords have a history of contribution to the game and have broad authority to develop game systems and steer the game direction. With regards to paperwork, the position of 'lord' does not necessarily carry with it a management burden. Only those who are listed as being part of the domain administration will have to push paper around. Finally, there is the top rank the High Lords. There are only a few of these, and they are all members of the Admin domain. High Lords are responsible for promoting Lords and, on rare occasions, additional High Lords. They're responsible for setting broad MUD policy, and ensuring that everything progresses in a way that is harmonious with the overall MUD philosophy. The last domain we have is the Cemetery domain, and it's kind of a holding area for people who are creators but don't have a day to day development role on Epitaph. They are usually ex-Epitaph creators, or creators from other MUDs who are given honourary developer status here, much like diplomats. Some of these members may have higher rank than creator, which within this domain is largely honourific.

Invisibility and You


I have already made some points on the importance of being invisible. Invisibility though has a few more issues that Id like to address. Firstly, there are several levels of invisibility. You as a creator have access to level one, which makes you invisible to everyone except creators and above. Lords have access to invisibility level two (also known as lord invisible), which makes them invisible to everyone except fellow lords and above. High lords have access to level three (high lord invisible), which makes them invisible to everyone except fellow High lords. While you are invisible, people cannot target you and cannot see you on the finger 19

Epitaph Online

http://drakkos.co.uk

output. Additionally, your finger info will not display you as being online. You have the ability to add exceptions to your invisibility so that your friends can still interact with you, and thats done by using your allow command. For example: allow drakkos Using the command allow by itself will list your current allow list. To allow an individual, you use allow <name>. If you want to remove them, then disallow <name>: disallow drakkos There is a certain etiquette to being an invisible creator. Its considered rude to send tells or souls to people not on your allow list, for obvious reasons (they have no way to respond). It is also considered somewhat rude to chat on a talker channel when you are invisible to the majority of your audience. Your may find that your domain leader is sometimes inaccessible to you by virtue of being at a higher level of invisibility please do not take this as an insult. There are often additional calls on an individuals time as a domain leader, and it is sometimes necessary to make onesself invisible to deal with these issues without interruption. If you wish to contact a domain leader who is on a higher level of invisibility, you can always send them a mail. Invisibility is a tool, and it is obviously not to be abused. Although you cannot be seen in the MUD while invisible, that doesnt give you the authority to snoop on other players. If you are caught doing so, there will be ramifications. Your invisibility is only an aid to effective coding, not the fulfillment of so many teenage fantasies.

Your Creating Environment


We're going to get to actual creating soon (ish), but before we do let's spend a little time talking about how you actually write code for Epitaph. You have two routes to development available to you. The first is to make use of the in-mud editors for code. I strongly recommend against this so strongly, in fact, I'm not going to mention the possibility again. You have the option available, but youre on your own if you want to follow through with it. The second method is to use an off-line development system. This will consist of two things: A decent text editor (note, not a word processor) A solid FTP client

For maximum ease of use, you want both of these combined into a single package if you're willing to splash out a little, I recommend UltraEdit very strongly. 20

Epitaph Online

http://drakkos.co.uk

Another popular choice is EditPlus. For Mac users, TextWrangler is a good choice. If you prefer to use separate packages, then Notepad++ and Filezilla are free, and excellent, packages. Regardless of which solution you go for, the basic system is the same. You write some code, you upload it to Epitaph, and then you update the in-game file. Epitaph's file structure may take some getting used to, so it's worth spending a little time navigating around. Every creator on Epitaph starts off with a home directory that is accessible in the /w/ directory. You have full access to write to this directory, and it's most likely where your first few files are going to go. The code that belongs to your area domain is found under /d/, in a subdirectory that matches the name of the domain. If you were a creator in Game for example, the code that belongs to the Game domain may be found in /d/game/. You navigate between directories using the cd command at all times, you have a working directory, and that's the directory you'll see when you list the files in it (you can do this with the ls command). The cd command stands for change directory, and ls stands for list. Your default directory is going to be /w/your name, and that's the directory you will log into when you open up your FTP client for the first time. For the purposes of the rest of this section, we'll assume you're using Ultraedit if you're using a different setup, you'll need to figure how how to incorporate all of the following information. From your Ultraedit application, go to File on the menu, and down to the FTP/Telnet option. That'll open up a submenu, of which you want the FTP Account Manager. Choose 'add account', and enter the details as follows: Setting Server Port Username Password Value drakkos.co.uk 6001 (if your ISP blocks this port, try 6999) Your Epitaph creator name here Your Epitaph creator password here

Commit these changes, then go to File -> FTP/Telnet -> Open from FTP and 'Browse Site'. If that works, beautiful you're ready to do some coding. If not, you may be having connection issues. Go back into the account manager, click on the 'Advanced' tab and check 'Passive Transfers' and 'Use IP Address returned by server'. Now, let's make sure you can edit files on the MUD. Double click on 'workroom.c' and you should get a copy of your workroom file downloaded to your editor. We're not going to do much to this right now all we're going to do is put in an 21

Epitaph Online

http://drakkos.co.uk

extra item to look at to make sure our setup is working. In your file you'll see a comment that says something like this:

/* * * *

The items are the things in the room that you can look at. Item longs should be proper sentences and shouldn't have \n at the end. Make sure you have a room item for every noun described in the long of the room or in the long of another room item. */

This is a comment, it's a bit of the code that is there for you to read the MUD doesn't pay any attention to it. Underneath this comment, we're going to add a little test item add this, *exactly* as it is written below: add_item ("textbook", "It's full of knowledge!"); Now, save your file. That commits your changes back to the MUD. You should always make sure you work from a fresh copy of the file each time you sit down you can't be sure that changes haven't been made to the file in your absence. Your change won't automatically be reflected in the MUD first you need to update the file so it loads the newest version. You can do it by updating the path of the file: update /w/your_name_here/workroom Or if you're standing in the room, you can simply: update here Assuming you've done this properly, you'll see something like this: Updated workroom of your_name_here (/w/your_name_here/workroom). If you haven't done it properly, you'll see lots of red and blue text. If this is what you encounter, go back to the editor and make sure your addition is exactly as shown above. Once you get the updated successful message, then type: look textbook All being well, you'll see the description you set for it echoed back to you. Congratulations, you're now ready to start writing code! We won't talk about that just yet, there's still more we need to discuss... but when it comes time, you're ready to roll.

22

Epitaph Online

http://drakkos.co.uk

Creator Commands
So, now weve spoken a bit about whats expected of you as a new creator, lets talk about the fun and exciting new powers you have available to you. First of all, lets talk about how you move around the game. Your usual navigation co mmands work as before, but creators are lazy and so we also have the ability to teleport ourselves instantly to any other part of the game. We do this using the goto command, followed by the path to that room. To go to your workroom, for example, you use the following command: goto /w/your_name_here/workroom Its worthwhile aliasing this under something like home, so you can always get to privacy. Hit return and youre magically there. If you want to return From Whence You Came, you use the goback command. Youll see the path of rooms along the top of the long description as you walk around make a note of any location you find interesting on your travels so that you can whiz around as you desire. Goto also lets you teleport yourself instantly to another player or creator who is online. Consider the etiquette of this before you do it people wont appreciate being dropped in on in their own personal workspace without some warning. If someone is in a public place, then by all means feel free to use them as a shortcut to finding the path to a particular room, for example: goto drakkos Thats just an example of the syntax. Seriously, dont use me as your Goto Guy (teehee), or I will devise some suitably extravagant punishment and mete it out to you over the course of your entire time on Epitaph. Ahem. You can also bring other people to you with the trans command, for example: trans drakkos You should exercise caution with this. Im sure it goes without saying that you shouldnt abuse this command to pull people around the game, but a special case might exist in terms of bringing players to your own private workroom. I cannot give blanket advice on this as the policy varies from domain leader to domain leader check with them first before you do it. Its not necessarily a good idea in any case it carries with it connotations of privilege and favouritism, and gets in the way of the normal functioning of the game. While in a creator workroom, players are free from PK retaliation or in-game punishments, and this can breed resentment. 23

Epitaph Online

http://drakkos.co.uk

As a further note, when you are going to trans someone remember your manners - let them know you want to trans them, wait until they tell you it's okay, and respect their wishes if they do not want transported. The obvious exception to this is in cases where Creator Justice is being applied, but that's not something with which you're going to be involved. While the policy on this will vary between domains, as a universal rule you do not trans players into development or play-testing areas without the express permission of the relevant domain lord. Your own areas in your /w/ directory are the sole possible exception to when you may bring players into the creator environment. When they are leaving, they should be returned to the exact same room they left with the following command: trans drakkos back Remember, check first with your domain leader to see whether or not they permit this. You no longer have the need for Mortal Possessions, but you can create copies of objects to which you know the path using the cl command (short for clone). Youve already done that with your bulletin board of course if you want to know the path to other items, you can use the pathof command if you have them handy. The pathof command may give you a response with a hash sign and some numbers at the end ignore those numbers for now, theyre not relevant for the time being. If you want a copy of an item that exists in the game, you can make use of the request command. Check out the syntax of the command for details, but you can list items in certain categories, such as: request list clothes You can also search within a category by providing a search string. If you want to list only those elements in the list of clothes that have the word a-line in their description, youd use this: request list clothes a-line When you find something you want, you request it by its name (replacing underscores with spaces) request thin cloth nappy At that point, itll appear in your inventory or, if you cant pick it up because its too heavy, itll appear in your current room. If you want to check the inventory of another player, you can use the inv command. Be careful with this, though it can be considered a breach of privacy: inv drakkos 24

Epitaph Online

http://drakkos.co.uk

If you want to make a copy of something another creator or player has in their inventory, you can use the dup (duplicate) command... for example: dup hilarious scottish bonnet in drakkos Assuming there is an item matching the name hilarious scottish bonnet, youll get a copy of that item in your own inventory. The usual rules apply to this though dont abuse it. Sometimes people have deeply personal items in their inventory (such as items with personal inscriptions), and it is considered a Grave Infraction to violate that privacy. You can dup things in your own inventory, and also dup generic NPCs (they will be cloned into your environment, rather than your inventory). You dont even need to be in the same room, they just need to loaded somewhere on the MUD. For example: dup zombie Before you dup things, it's worthwhile to get the path of the item (using the pathof command) to make sure you're getting the thing you actually want. When you 'dup black shirt in drakkos', you may find that you didn't want a peep-hole black leather gimp shirt - you actually wanted just the normal black shirt that normal people wear. Pathof will let you see where the item resides, and will also help you learn where you need to look for particular things. As a general rule, you have access to read any of the files in your domain (except for those in the secure directory), and usually in other domains too. You can do this using the more command if you know the path, like so: more /w/your_name_here/workroom.c If you are standing in a room, you can get the code by using here in place of a path: more here If you want the code of an item in your possession, or an NPC that is loaded on the MUD, you can refer to it by name: more gun If you want to get rid of something, you use the dest command (destruct): dest sword Its possible to dest players and creators, but dont do it unless you have a valid reason (as a hint, if you dont know if you have a valid reason, you dont have one).

25

Epitaph Online

http://drakkos.co.uk

Creator commands often make use of a powerful matching system called 'wiz_present' - this has a helpfile of its own if you want to check it out. It's a much more powerful matching system than you will have been used to as a player where you could usually only target things in the same room (with some notable exceptions). Wiz_present allows you to match things according to far more complex criteria, and is MUD-wide. You've already seen some of this in action with the commands above, but there are some useful expansions. Would you like to look at the code of the room in which I'm standing? You can refer to the container of an object by enclosing its name in brackets: more (drakkos) That gets you the code of the room I'm in, rather than the code for me myself. If I want to get access to something in a container or in another player, I can refer to things as such: pathof sword in drakkos You can even chain these together: pathof sword in (drakkos) There are more complicated things you can do with wiz_present, and these will come with time. That should be enough in the short term to get you navigating around and setting yourself up with some items the next thing well talk about are your two most powerful commands. The first of these is call and is used to make things happen in objects. Youll need to know a bit more about coding before you can use this effectively, but for now lets do a simple example and give ourselves a few levels of a skill: call add_skill_level ("combat", 100) me The first part is the name of the method were calling (add_skill_level), the bit between the brackets are the parameters (the skill we want to increase, and how many levels by which we want to increase it), and the last bit is the object on which we want to call the method (in this case, ourselves). Again, a word of warning dont use this on players unless you know youre doing the right thing. We Are Always Watching. You dont need to know much about call at the moment, just know its a powerful tool well talk about in good time. The second command is exec its a more powerful tool than call, and correspondingly more complex. It lets you execute a bit of code on the fly without having to build it into an object. Once again, dont abuse this people are paying attention. 26

Epitaph Online

http://drakkos.co.uk

There are dozens of other commands you have available as a creator most of them have helpfiles to go with them. If youre interested to see whats available, try this: commands This command will reveal a list of the commands you have available, and one of the categories of command will be 'creator'. You'll see something like this:
Your Creator commands: addalt, addevent, altonline, autodoc, autodocadd, autodocrm, ban, blackball, bugreps, build, callouts, cheaters, check, cloner, clubs, comment, compare, compare2, coords, cos, countshadows, creator, cstat, ctotd, dbxwhere, debug, define, delalt, delevent, denied, destruct, deutf8, dirs, distribution, dump, dupdate, effects, errors, family, fds, fetch, findcorpse, findredef, fixed, flush, forwards, ftpwho, gag, gna, goto, goback, gopop, gopush, guildlist, hcode, hcre, head, hliaison, home, host, hours, housing, hupdate, iemote, ilocate, imc2, imc2mudlist, inheritance, inv, jumps, localcmd, maestro, mail, malloc, materials, mediawiki, memstat, meminfo, mkdir, more, mobs, mudinfo, multiplayer, mv, netstat, netdups, newest, nicetime, nslookup, object, ogrep, oldest, online, osql, pathof, people, permit, playerinfo, pquests, prof, project, pstat, ptesters, pwd, qpeople, qwho, randomname, reboot, rehash, repcomment, request, review, rm, rmdir, sar, schematics, screen, set, setmin, setmmin, setmmout, setmout, shadows, showhelp, shutdown, skillcheck, snetstat, snoop, snoopers, sockets, soulinfo, spawn, spell, sql, sqlcmd, stat, stats, status, suspend, taskmapper, tcheck, tell!, template, terms, terrains, testchar, todo, trans, trace, ungag, unsuspend, unventriloquise, upgrade, usage, wgoto, whereis and which

All of those are available to you (sometimes with restrictions), just be careful that you know what they do before you use them. If you use MXP, clicking on the underlined command will give you its helpfile. As a final note, youre going to die quite a lot some of it is general horseplay amongst creators, sometimes its for testing purposes, and sometimes its just by accident. It probably goes without saying that death for a creator is Not a Big Deal. Oh, and as an extra perk you have access to mail wherever you are in the game. Just use the mail command and youll find yourself inside the Epitaph mailer. Finally, it's easier to be a creator when you don't need to deal with the limitations of normal players. There are four options under 'creator' in your options command that are especially useful:

27

Epitaph Online

http://drakkos.co.uk

Option suppress_aggressive suppress_maestro

Effect NPCs will no longer be aggressive towards you. Maestro will no longer pick you out for his tender ministrations. No matter where you are, your temperature will be 'comfortable' You will no longer experience gains or decays in your wellbeing.

suppress_temperature wellbeing_freeze

I would advise switching all of these on, but the choice is yours.

Alts and You


Many creators find the role of creating gives them a satisfaction that playing never did, but it's not a good idea to let the development part of your life on Epitaph dominate entirely. You are allowed to continue playing with an alternate character indeed, it should be encouraged lest you start to become out of touch with how the game actually works. As usual though, there are some restrictions on this. First, your player character should not participate in any player-run element of the game. Your loyalty, first and foremost, is to being a creator. Even when online as a player, that loyalty is paramount. As far as possible, you should play the game as a player that means no sneaking looks at the code while playing. Obviously you may be more familiar with certain bits of codebase than others, and you can't simply forget what you already know. Try to make an effort not to turn to the code whenever you encounter a challenge instead, consider how the behaviour of the MUD could be improved and make a suggestion. If you need to rely on the code, then consider how difficult it would be for a player without your privileged access. It's natural that your first thoughts as a creator will be flavoured by your experience as a player that will go away with time. Try to take a broader view when playing, and see things from the perspectives of game balance rather than from the narrower view of your player character. On the other hand, your perspective as a player is important too you'll have your own views on what's important to the game, what you enjoyed, and what you didn't. That perspective is valuable - it just has to be tempered with concerns for game balance.

28

Epitaph Online

http://drakkos.co.uk

You are also permitted a test character to help you debug code. As a convention, your test character should have an obvious name, such as <your_name>test, or your name backwards. Once you've created your test character, you can use the 'testchar' command to add the necessary properties to him/her: testchar draktest on Your test character can be logged on at the same time as your creator character. Indeed, that's the only time they should be logged on. You can do anything you like to your test character change their skills, give them vast amounts of money, kill them on a whim. What you can't do is play the game with your test character any attempt to do so will be treated as an attempt to cheat, and you'll end up being dismissed

Epitaph Development Cycle


Development on Epitaph tends to follow its own idiosyncratic development cycle. Again, each domain does things differently, and the approach a domain takes will vary from project to project. Theres a general structure to the process that can serve as a useful guide to what things actually mean. To begin with, there is a plan. That plan may be very detailed in terms of what code is to be incorporated, or it may be very vague (lets build a city!). Usually the plan is part of a larger master plan that incorporates the Domain Leaders vision for their domain. The plan may not even be written down anywhere, and may exist in just the Domain Leaders head. If youre lucky, the plan will be documented somewhere so you can see the big picture. The Plan needs developers thats where you come in. Your domain administration will likely assign you to a project (perhaps with your input as to your interests, perhaps not) and give you a broad remit for what it is you are to do. You then work through this project, writing the code that goes with it. Although you will probably have to get specific authorization for certain developments (such as quests you want to write and add), on the whole you usually get pretty free reign to do what you want, within reason. Its likely that your domain administration will check up on your progress from time to time, but the responsibility for getting everything done falls to you. A lot of people find this hurdle difficult to clear, so if you feel as if you are falling behind on this score you should contact some more experienced creators for advice and guidance. Its easy to feel that youre all alone in your development, but thats not true you have your fellow domain creators to call upon, and by the time youre developing your first real project youll have gotten a better feel for the culture of creatordom and be more comfortable in participating fully in the social side of development. Eventually, your project will be completed, and then its on to your domain administration for approval the formality of this as a process will depend on how 29

Epitaph Online

http://drakkos.co.uk

active your domain administration has been in following your progress. Once approval has been given, the next stage for your project is playtesting. Whether it can be playtested right away is going to depend on where your project fits into the larger plan of the domain if its part of a larger project, it may have to wait. If its largely contained, you may be able to get it playtested instantly. Once your project has left playtesting, there are three usual destinations one is back into development if you and your domain administration feel it requires more creator attention. The second is into limbo, where the implementation of your project in the game proper is dependant on other factors (like other projects being complete). The third, and happiest outcome, is into the game for players to enjoy. At this point, you can wallow in the admiration and praise of your satisfied players, and consider whats next. Of course, its never all admiration and praise, you just need to realize that no matter what you do, you cant keep everyone happy. Some people live to complain. There is a tool available to help you keep your domain administration informed as to where you are with your projects its the Epitaph Project Tracker, available from the creator section of Epitaphs web-pages. The use of this tool may or may not be mandated by your domain administration, but every project going into playtesting needs to be recorded with this handler. Check with your domain leader to see what the specific domain policies are regarding this. You also have a secondary way in which to document your project, and thats through the creator Wiki, also linked from the Epitaph creator web-page. You should spend some time coming to grips with how to use it, and contribute to your own domain Wiki especially. Depending on how heavily used the Wiki is within your own domain, you may find it full of interesting plans that will help you gain a wider perspective of whats going on in your domain. Its also a good way to get people interested in what youre doing although not everyone reads the Wiki, much less contributes, there are enough Compulsive Wiki Watchers that your words wont go unread. I personally am a huge fan of the Wiki, and of Wikis in general, so if you want to talk Wiki with me, please do so!

Conclusion
What you will have undoubtedly have taken away from this document is an sense of the responsibilities you have undertaken its okay to feel overwhelmed, theres a lot that you need to learn, and you may feel as if you dont have a lot of time in which to do it. Theres time, though and there are people on hand to help you every step along the way. We havent spoken at all about coding yet thats still to come. At the moment, we just want you to spend some time thinking about how this new and interesting step of your experience on Epitaph is going to work. Spend some time snooping around get a feel for what has come before, and whats planned for the future. 30

Epitaph Online

http://drakkos.co.uk

Youre part of a large and interesting team of various levels of activity were all in this together, and regardless of how much we may bicker and argue, each and every one of us wants whats best for the MUD. Id also hope that what youre taking away from this document is more than just a sense of foreboding, but also a sense of excitement. Its a lot of fun being a creator, and a lot of the Dire Warnings in this document are to ensure that you dont fall foul of some of the more common mistakes. Wed like you to enjoy your time as a creator, and thats hard to do if youre always afraid of what youre doing. Remember that the members of your domain administration are not just there to ensure that youre working theyre there to guide you too. Look to them for support, as well as to your fellow domain creators and those of us in Learning. Theres plenty of support to be had, you just need to be willing to look for it. Of course, that doesnt take the overall responsibility for your success off of your own shoulders we can only take you so far, the rest is up to you!

31

Epitaph Online

http://drakkos.co.uk

Section One

Introductory LPC
32

Epitaph Online

http://drakkos.co.uk

Introductory LPC with the Epitaph lib


Introduction
In this set of introductory creator material, we are going to talk about the way in which the gameworld of Epitaph MUD is constructed. This is a book about coding, but don't worry about it - we're going to introduce the topic as gently as we possibly can. New topics will be introduced only when they help us make our code better and more interesting. You will find all of the source code that goes along with this text available in the /d/support/deadville/ directory. It is broken up into chapters, so you can see exactly what your code should look like at each stage of development. You can goto the room 'access_point' within this directory to explore all of these versions and see the differences between them.

The Learning Scenario


Over the course of this text, we'll be building an area exactly like the kind you have experienced in the game itself. We're going to create rooms, NPCs, items, and shops. We're going to stop short of adding unique functionality or quests, because that's a part of the follow-up creator LPC section. By the end of this section you should be comfortable with the basic building blocks of LPC. Along the way you'll even learn a bit about code, but only a bit. Being an Epitaph creator involves working with code, but you don't need to be a coder to be a creator. Enough to get by will take you far, although it's always good to learn more if you can. Our learning scenario is the development of the village of Deadville. A fully completed version of this may be found in the learning domain under /d/support/deadville/mapping_it_out/. I would like you though to construct this step by step in time with the teaching material you only get a fraction of the benefit from reading completed code that you get from writing the code as you go along. Deadville is a simple village, with basic facilities. It also doesn't contain much in the way of descriptions - if you want to see what good descriptions look like, take a look at some of your favourite game areas. Our scenario is for teaching you how to put an area together. It is assumed that you'll be able to write the text yourself. Some guidance on the topic of writing descriptions may be found in the Being A Better Creator section. What we're interested in here is the structure of the area, not how it looks. We're going to approach this functionality according to a development technique called incremental development. I heartily recommend this for beginners because it greatly simplifies the job of building complex programming structures. This is a process of beginning with something very simple until you have the very 33

Epitaph Online

http://drakkos.co.uk

basic framework fitting together. Once you have this skeleton of code, you can start adding features to it a little bit at a time. In this way, you avoid becoming bogged down in the overwhelming complexity of a project you get to see it grow all around you from the most humble beginnings to a complete and fully featured area. To put this in context, we start with a single room that does nothing exciting. We then add a second room and connect these two skeleton rooms together. Then we add in another, and another, until we have a whole framework of a village ready for us to fill in the blanks. What we don't do is create the whole framework to start with and then become flummoxed when it doesn't work. Incremental development is an exciting and satisfying process, and one with which you are going to become entirely familiar as you work your way through this material. You can see your developments shaping up in a very tangible way, and that invites further development. Trying to do everything to start with can be a soul-crushing way of developing a project.

The Structure of the Game


Everything on Epitaph MUD is something called an object. Obejcts are quite a complex topic in themselves, so we're only going to scratch the surface of this for now. Every room is an object, every item is an object, and every player is an object. Confusingly, object doesn't mean that it's necessarily something you can touch or interact with in the game. Your combat commands for example are also objects. It's not an easy thing to get into your mind to start with, but it'll become easier with time. In programming terms, an object is a structure that contains some data, and instructions for acting on that data. Some objects also take on the properties of other objects through a mechanism called inheritance. This is the same general biological principle you'll be familiar with in real life, only cast in programming terms. In real life, a cat inherits the properties of being a mammal, which in turn inherits the properties of being an animal. We'll see this mechanism in action from the very first code we write - our object inherits the properties of being a room. Most of the code you need to work with on Epitaph has already been written for you you just need to tell the MUD what things you want to happen, and when. Most of the things you'll want to do, at least in the short term, are available for you. When you want to get a little more adventurous, then you'll be looking at moving onto the more advanced creator material. The MUD itself is built on three separate structures. The first of these is the driver, which is known as FluffOS. You won't need to worry about that just now. The second part is the mudlib, and it defines all of the code on which the rest of the game - the domain code - is built. It is domain code that you will be building in the short term, and that's all the code you'll find underneath the /d/ directory. The mudlib is everything else, including code that will Scar and Terrify you if you read it too early. As a basic guide to the relationship between the mudlib and the driver, it can be 34

Epitaph Online

http://drakkos.co.uk

summed up like this the mudlib knows what to do, but doesn't know how to do it, and the driver knows how to do it, but doesn't know what to do. Domain code includes all of the areas in which you adventure as a player, all the quests you have encountered (and the ones you haven't), and all the NPCs you've slain. When an object is made available in the game, it must first be loaded. At that point, it becomes a master object. Many objects remain as master objects, which means only one of them is ever in place in the game rooms and unique NPCs are good examples of these. Some objects are clones, and that means they are copies of the master object. Generic, common or garden NPCs and items are examples of cloned objects. When we need a new pirate for example, we just take a copy of the master object and put that copy in the game. That copy is a clone of the master object. In order for an object to load, we use the update command. If our code is all Present and Correct, the object will load. We'll look at this more in the upcoming material. If an object has been incorrectly coded, you will be presented with lines of red text (syntax errors) and blue text (warnings). We call these compile time errors - every syntax error in your code must be fixed before an object will load, but it will load if warnings are present. Fix them anyway - they don't break anything (usually), but they can make your code behave in unexpected or inefficient ways. The process of writing code, updating an object, and then fixing the syntax errors will become second nature to you before too long.

Programming as a Concept
Programming is a difficult task, unlikely almost anything you may have learned before it's like learning how to do mathematics in a foreign language a confusingly new syntax combined with a formally exact vocabulary. When you learn how to speak a different language, you can rely on your interlocutor to be able to divine meaning even when you make mistakes a computer cannot do that. And worse, it does exactly what you tell it to do that sounds great in theory, but it can be hugely problematic in reality. Computers don't understand much at their basest levels, they are machines for manipulating electrical impulses. At the lowest level, these are interpreted by the computers as a collection of ones and zeroes (a numerical system called binary, but you don't need to know anything about that just yet). However, we don't want to write our programs as ones and zeroes it would take a long time, require a lot of tedious busy work, and generally be a massive drag. The first programmers however did exactly that, they wrote their programs in the language of the computer itself - machine code. As time has gone by, it has become simpler to write programs for computers in order to manage the complexity of programming as a task, computer people developed successively more abstracted programming languages, and introduced the idea of compilation. Rather than writing in machine code, programmers could 35

Epitaph Online

http://drakkos.co.uk

write in an easier, simpler system and then make the computer itself convert that simpler representation into machine code. This conversion process is called compilation. The first attempts to do this were through a language called assembler, which is only a little bit better than machine code directly. Assembler is known as a low level language because it's not all that far removed from the low level grunts and snorts of machine code. As the years have gone on, more and more programming languages were developed. Successively they abstracted writing programs into more and more English-like syntax. Eventually a language named C came along, and revolutionized how people thought about programs. C was so popular that most of the successful programming languages of modern times use its style of writing code in one way or another C#, C++ and Java especially. These are known as high level languages because of the level of abstraction involved. Computers do not understand the code written in these languages, but the process of compilation converts the code into something the computer can understand. On Epitaph, we use a language called LPC which was created by Lars Pensjoe. While it has very similar syntax to all of the above named languages, it has a host of unusual and useful features. We'll get to what those features are as we go through the material. Programming is essentially the task of taking a list of instructions, and writing them in such a way that the computer can follow your instructions to complete a task. To give a simple, real world example imagine the task of making scrambled eggs. Imagine too that you have an obedient, but simple-minded kitchen assistant, and you would like them to make some scrambled eggs for you. They do everything you tell them to do, and they do it exactly as you tell them. It sounds like an ideal situation, but consider the following instructions: 1. 2. 3. 4. 5. 6. 7. 8. Get two eggs from the fridge Get some milk from the fridge Get some butter from the fridge Break the eggs into a jug Whisk the eggs until they are well mixed. Heat some milk and some butter in a pot. Add the egg mixture. Stir until scrambled eggs are made.

The instructions are pretty simple, but your Simple Minded Assistant falters at the first instruction. He, or she, stands vacantly at the fridge, pawing ineffectually at its surface. Cursing, you amend your instructions a little: 1. 2. 3. 4. 5. Open the fridge, you insensitive clod. Get two eggs from the fridge Get some milk from the fridge Get some butter from the fridge Close the fridge 36

Epitaph Online

http://drakkos.co.uk

6. Break the eggs into a jug 7. Whisk the eggs until they are well mixed. 8. Heat some milk and some butter in a pot. 9. Add the egg mixture. 10. Stir until scrambled eggs are made. You set your Simple Minded Assistant back to work they open the fridge. They get two eggs out... success! Then they stand there with a confused look on their face. "What does 'some' mean?", they ask. You curse once more: 1. Open the fridge, you insensitive clod. 2. Get two eggs from the fridge 3. Get two tablespoons of milk from the fridge 4. Get a knob of butter from the fridge 5. Close the fridge. 6. Break the eggs into a jug 7. Whisk the eggs until they are well mixed. 8. Heat some milk and some butter in a pot. 9. Add the egg mixture. 10. Stir until scrambled eggs are made. And then you start them on the task. "Uh, the fridge is already open", they say, and then the metaphysical complexity of the situation causes them to shut down until you amend the program a little further: 1. If the fridge door is shut, open the fridge, you insensitive clod. 2. Get two eggs from the fridge 3. Get two tablespoons of milk from the fridge 4. Get a knob of butter from the fridge 5. Close the fridge. 6. Break the eggs into a jug 7. Whisk the eggs until they are well mixed. 8. Heat some milk and some butter in a pot. 9. Add the egg mixture. 10. Stir until scrambled eggs are made. And so on, until you have a completely correct, entirely unambiguous list of instructions that any dolt could follow. This is the essence of what programming is all about. The only difference is, your instructions are given in LPC, and the Simple Minded Assistant is the MUD itself. Trust me, it's even more stupid than your kitchen assistant. This is a much trickier task that I'm making out here and the only thing that makes it easier is practice - lots and lots of practice. That leads us to the next section of this chapter...

37

Epitaph Online

http://drakkos.co.uk

It's All Down To You


Whether you are learning to code on Epitaph, or in a more formal setting like a university course, the basic proviso is the same nobody can teach you how to code. Sure, people can point you in the right direction and provide you with ample reading and reference material. What they can't do is make you understand, because you don't learn it by having someone explain it to you. The only way you learn how to code is by practicing. But more than that the only way you really learn is by trying and making mistakes. If you get it right the first time, that's great but you would have learned more by getting it wrong and working out what why your code didn't function the way you hoped. I guess what I'm saying is don't get discouraged. You are going to make mistakes, and sometimes you're not going to know how to fix them. That's what the your domain leader and fellow domain creators are there to help you with but please do try to work it out first before you ask for help. Not because they don't want to help you, because they do it's just you'll learn more if you work out the answer yourself. You've got to be willing to persevere in this the easiest way to fail is to not try. We can support you in your crusade to be a Good Creator, but only if you meet us half way. Remember, your domain lord thinks you can make it as a creator, or you wouldn't have been hired in the first place.

Conclusion
In this chapter we've gone over the very, very basics of what programming actually involves, and how code on Epitaph is put together. All of this is useful knowledge, but it's not coding yet. That comes in the next chapter when we look at building our very first room. Are you excited? Touch your nose if you are!

38

Epitaph Online

http://drakkos.co.uk

My First Room
Introduction
Right, let's get started for real then we've got a lot to learn and you've already read so much without getting your hands on a bit of code. Now we're going to change that by creating our very first room and filling it with some of the simpler elements available to a novice creator. This is a big step - even the largest of our area domains started off at one point with a single room. Once we've gotten that first achievement under our belt, we'll talk a little bit about structuring an area so that we don't get hopelessly bogged down in the minutiae. We're going to look at the basic skeleton of Deadville, and how many rooms we're going to develop having a plan always makes it easier to schedule your time and effort. You should always sit down and sketch out what you are hoping to accomplish with an area before you start writing any code. The easiest time to make corrections is at the conception stage. All the examples from this chapter can be found in /d/support/deadville/my_first_room/ - all the paths in there will be based on /d/support rather than /w/your_name, because well, that's where it lives!

Your Own Deadville


The assumption throughout this material is that you are going to be building this village alongside the tutorials. In order to link up what's done in these documents to what you're doing on the MUD, we need to calibrate! That's just a fancy-pants way of saying that we're all using the same directory names. First, you create a directory in your /w/ drive called Deadville. All subsequent directories will be created in here. You don't need one for each chapter as is present in /d/support/learning/deadville - you should have one copy of this that grows and changes as you develop it further. mkdir /w/your_name_here/deadville Once you've gotten the directory, you want a subdirectory in there called rooms: mkdir /w/your_name_here/deadville/rooms/ From this point on, this chapter will assume this is where you are uploading your files. Make sure it is, because otherwise you're going to have lots of problems ensuring that the paths you use map up to the paths we use in here.

39

Epitaph Online

http://drakkos.co.uk

The Basic Template


First of all, let's set up our basic template for creating a room. Create a new file in your development environment (See Welcome To The Creatorbase for a discussion on that if you haven't already done this), and type the following into your editor:
inherit STD_OUTSIDE; void setup() { set_short ("simple village road"); add_property ("determinate", "a "); set_long ("This is a simple road, leading to a simple village. There " "is very little of an excitement about it, except that it represents " "your first steps to becoming a creator!\n"); set_light (100); }

Save this file in your /rooms/ subdirectory, and call it street_01.c. Now, in the last chapter we spoke a bit about loading objects this room doesn't yet exist on the MUD, even though it exists on the disk-drive. To see if it loads, we use the update command: update /w/your_name_here/deadville/rooms/street_01.c When you hit return, you should see a message something like this: Updated simple village road (/w/your_name_here/deadville/rooms/street_01) If it doesn't, then something has gone wrong with what you've typed into the editor. Look very closely at the example code above and see where your code differs it should be identical (if it is identical, it will work). If all else fails, simply copy and paste the code above into your editor - don't get bogged down in the errors just yet. You'll get your fair share of those later when you try to do things without a safety net. Once your room has loaded, you need to move to it with the goto command: goto /w/your_name_here/deadville/rooms/street_01 If all has gone well, you'll see the following: /w/your_name_here/deadville/rooms/street_01 (unset) You can hear: Nothing. In the distance you can see: Nothing. This is a simple road, leading to a simple village. There is very little of an excitement about it, except that it represents your first steps to becoming a creator! 40

Epitaph Online

http://drakkos.co.uk

It is a freezing cold winter night with heavy sleet and thick black clouds. There are no obvious exits. Congratulations! You've just taken your first step into a larger world! Now, lets talk about what all of that actually means. Let's take a look at what you've done and explain what each of the different bits mean. Most of them should be fairly obvious, but it's important that you know the bits you don't need to worry about:
// This the object that defines all of the functionality of which we're // going to make use. All of the code that we use in setup is defined in // this object. In this case, it's an outside room... so it gets all the // usual weather strings and chats. inherit STD_OUTSIDE; // This is a function (we'll come back to this topic later). All you need // to know for now that the code in setup gets executed automatically by // the MUD when your room is loaded. void setup() { // The short description is what you see when you 'glance' in a room. set_short ("simple village road"); // The determinate is what gets appended to the short description // sometimes. In this case, this room becomes 'a simple village road'. // If we wanted it to be 'the simple village road', we'd change // the determinate here to 'the'. add_property ("determinate", "a "); // This is the long, wordy description of the room that you see when you // have the MUD output on verbose. set_long ("This is a simple road, leading to a simple village. There " "is very little of an excitement about it, except that it represents " "your first steps to becoming a creator!\n"); // This sets the current light level for the room. 100 equates // to 'bright sunlight' set_light (100); }

The lines that start with the // symbol are comments. The MUD completely ignores these when it is loading your room they are there purely for humans (and other creators) to read. It's good practice to comment your code properly, but that's a topic for a later day. You'll read more about commenting in Working With Others. At the simplest level, all of the code that you incorporate into your rooms goes into the setup of a room. You need to pay particular attention here to the curly braces it's easy to get these wrong when you first start all of your code goes between the opening curly brace {, and the closing curly brace }. That's the basic structure of each room we're going to create you may want to copy and paste this somewhere you have easy access to so you can use it for each room you start.

41

Epitaph Online

http://drakkos.co.uk

My Room Sucks
It sure does, little buddy! Your room is dull, boring, uninteresting . Its generally lame and horrible, and something you couldnt show anyone with a sense of pride. Thats okay, for now all we want is to ensure that it loads let's take one step at a time. Let's look at adding some more interesting things to your room. The first thing you'll notice is that you can't look at anything... 'look village' gives a rather disheartening response:
Cannot find "village", no match.

Let's start off simply and add some things for people to look at. We do this by using add_item:
add_item ("village", "The village looks untouched by the rampaging dead.");

This goes into the setup of your room as a matter of convention, it goes after you've set the long description for the room. Update your room, and 'look village'. Your response will be much more encouraging:
The village looks untouched by the rampaging dead.

Looking at the road will give us the 'no match' message, so let's add an item for that too:
add_item ("road", "You're standing on it, you dolt!");

You can add as many items as you feel is appropriate. In general, more is better but at the very least every noun in the main description, and every noun in the add_item descriptions should have an add_item of its own. But wait! We're not quite safe here, because the long description refers to a 'simple village', and what happens when we look at the simple village? Yep, 'no match'. We get around that by being more specific in our add_item definitions:
add_item ("simple village", "The village looks untouched by the " "rampaging dead."); add_item ("simple road", "You're standing on it, you dolt!");

Add_item is quite clever by doing this, we get the root noun (village) plus any adjectives we define, in any order in which we want them... looking at 'village' gives us the description, as does look at 'simple village'. If we had an add_item for 'shiny red simple village', we'd be able to look at 'shiny village', 'red village', 'shiny simple village' and any combination thereof. We'll return to add_item intermittently as we go along it's much more powerful than a lot of people realize, and we'll have good cause to make use of that power 42

Epitaph Online

http://drakkos.co.uk

in later chapters.

It's a little too quiet...


Most rooms in the game have a little more life to them than your first attempt. They have NPCs (we'll get to those), and they have chats that appear at semirandom intervals. These chats are a big part of making a room feel alive, so let's add some of those. Again, in your setup function, add the following code. It's slightly complicated, so you may want to copy and paste it:
room_chat ({ "It's "It's "It's }) })); ( ({ 120, 240, quiet... a little too quiet.", simple... a little too simple.", exciting learning to develop rooms!",

Yikes! What's with all the brackets and braces? Let's pretend we don't need to worry about that just now, because we don't it'll all come clear in later documents. Instead, we shall focus on the salient details here the first are the two numbers. These are pretty straightforward the first is the minimum time that must pass before a chat is sent to the room, and the second is the longest period of time that will pass before a chat. The strings of text are the actual chats that get echoed to the room you can have as many of these you like, inserted into the list. Each needs to be separated by a comma:
room_chat ({ "It's "It's "This "It's }) })); ( ({ 120, 240, quiet... a little too quiet.", simple... a little too simple.", is a chat in a list!", exciting learning to develop rooms!",

Note the quotation marks we'll talk about why these are necessary in a later chapter of the material. Just take my word for it now that they're needed. The only thing missing from our most basic of rooms is an exit to another room. For that, we actually need a second room to move to!

A Second Room
Let's start with out basic template as before, changing the long description a little to reflect the fact this is somewhere different. We'll call this one street_02.c, so 43

Epitaph Online

http://drakkos.co.uk

create a new file and save it to the MUD:


inherit STD_OUTSIDE; void setup() { set_short ("simple village road"); add_property ("determinate", "a "); set_long ("This is a little way along the path to the village of " "Deadville. The path continues a little towards the northeast " "towards the rickety buildings of the market square. To the " "southwest, the path wends away from what passes as civilization " "in this learning scenario.\n"); set_light (100); }

We now want to link these two rooms up this is done using add_exit. Add_exit requires three pieces of information before it can make up a connection to a room the first of these is in what direction the exit lies. The second is what path the next room may be found at. The third is the type of exit (is it a door, a path, or stairs?). Our long description above shows us what our direction is going to be our first room lies off to the southwest of this room. It's going to be an exit of type 'path':
add_exit ("southwest", "/w/your_name_here/deadville/rooms/street_01", "path");

This goes into street_02.c. Street_01.c gets its own exit leading to our second room:
add_exit ("northeast", "/w/your_name_here/deadville/rooms/street_02", "path");

Update both of these rooms, and you'll be able to walk between them. That is pretty nifty, I'm sure you'll agree! One thing to bear in mind is that while we've put the full path of these rooms in our add_exits, that's not how we should do it 'In Anger' because it makes it really difficult to move areas around as you need them. Later, we'll talk about the importance of properly structuring the architecture of your code so that it's easy to move things around various directories. You'll note that our second room is missing add_items and room_chats adding these is left as an exercise for the interested reader. You should be able to do that yourself referring to the notes for how it is done in the first room.

Our Overall Plan


So, that's two simple rooms linked up. It's worth making sure we have an actual plan here so that we know how each room fits into the whole. You may have your own favourite mechanisms for designing a map for a new area, I personally favour the humble text-file. This is the map of the area we're going to develop together:

44

Epitaph Online

http://drakkos.co.uk

A B | | 6-7 | | 3-4-C / 2 / 1

We're using a number here for each outside room, and a letter for each inside room. We thus need a key so we know which number is which room: Number 1 2 3 4 5 6 7 Filename street_01.c street_02.c street_03.c market_southwest.c market_southeast.c market_northwest.c market_northeast.c

We'll worry about the inside rooms later. It doesn't matter particularly what filename we give the rooms, but to ensure that your work and these tutorials sync up, you should be making use of the same filenames as we use. Trust me, it's just simpler that way, for now. A text file is ideal because it can be viewed on the MUD, or put easily into the wiki. Here, I've put a number for each room we're going to create, and letters for inside rooms with Further Features. It's a small area, but one that will cover everything we need to discuss in order for you to make a meaningful contribution to your domain. We've done room 1 and room 2. In the next section we'll add in the skeleton of the remaining rooms as we encounter new things we can develop.

Property Ladder
As part of our code above, we have a line that references to 'add_property'. Properties are little tags that can be added to an object whenever they are needed, but they don't actually do anything other than exist. We can check in other pieces of code whether an object has a property attached, and if so change the way we treat it a little. As an example of this working, you can add the 'gills' property to yourself to give you the ability to breathe under-water:
call add_property ("gills", 1) me

45

Epitaph Online

http://drakkos.co.uk

The first part of this is the property to add, and the second is the value to give that property. The 1 simply indicates 'yes, this property should be added'. Now, you'll never drown when you're in the water. Properties are used intermittently through the MUD, but you should be wary of them - they have a tendency to clutter objects. Because they are so easy for people to add (there is no set list of properties which are valid), it's tremendously easy for creators to write objects that attach properties to players and items, and then never write down why or what the property is for. Before too long, everyone is wandering around with properties like "gnah bag check", and no-one except the original creator (who may have left by this point) has any idea where it came from. Properties are a quick and easy hack, but you shouldn't use them too much. In circumstances where you do, you should always make sure that they are timed properties. You can do this by providing a third piece of information to the add_property call - the number of seconds the property should exist for:
call add_property ("gills", 1, 120) me

Here, the gills property will remain on you until 120 seconds have passed, at which point it won't. These are 'self-cleaning' properties and you should always, always use them unless you are very sure indeed that you don't need to.

Conclusion
These simple rooms aren't very interesting but we're going to change that as we go along. We have taken a big step though creating an actual real room you can stand in, and another room you can move to - it's from these basic building blocks that whole cities are built. We're going to leave them lacking in add_items and long descriptions and such - nothing is gained by writing these rooms to the standard that would be expected in the game. Once you've seen one add_item, you've seen them all - you don't learn more by having something repeated over and over again. Filling in the blanks is left as an exercise for the interested reader - I heartily encourage experimentation to see what happens when you change things - the fact that you have easy access to the original source-code from /d/learning/deadville means that there is no cost if you break everything beyond repair.

46

Epitaph Online

http://drakkos.co.uk

My First Area
Introduction
You've taken your first steps to developing a small room within the MUD. In this chapter, we're going to extend this to an entire area. In the process, we'll talk a little bit about how to develop an architecture that means we can easily move things around as we need, and link them up with minimum fuss. This is an important aspect of developing an area things get moved around a lot on Epitaph, especially through development and playtesting. Areas that are difficult to maintain are a drain on everyone's time. Luckily your areas aren't going to do that, because we're going to talk about how you can avoid it. Once we've looked at the setup of the skeleton of the area, we're going to spend a little more time looking at the exits we've setup. The more detail we put into our areas, the richer our game world seems. That's a goal I'm sure we can all agree on being worthwhile. All the example code for this chapter can be found in /d/support/deadville/my_first_area/

The Structure
You've seen the map from which we're going to be developing. We can develop room by room, only adding a room into the whole when it's completed. However, in order to get a feel for how things are going to link up, it's beneficial to put skeleton implementations of each room in place, and then incrementally add their descriptions and features. That's what we're going to do now. It's easier than you may think. Remember our map:
A B | | 6-7-C | | 3-4-5-D / 2 / 1

And our key:

47

Epitaph Online

http://drakkos.co.uk

Number 1 2 3 4 5 6 7

Filename street_01.c street_02.c street_03.c market_southwest.c market_southeast.c market_northwest.c market_northeast.c

We've got the basic outline of rooms 1 and 2. We have another five rooms that will be outside rooms, and then four that will be inside rooms. We begin putting this framework together with a template. Save it as street_03.c:
inherit STD_OUTSIDE; void setup() { set_short ("skeleton room"); add_property ("determinate", "a "); set_long ("This is a skeleton room.\n"); set_light (100); }

This is the room you're going to repeat for each of our outside rooms - there's nothing in it, but that's fine - we don't want to spend time writing some intricate, beautiful descriptions we are only going to have to scrap later. The four remaining rooms of our development are the market rooms. Now comes the magic! You don't need to painstakingly create a new file for each of these rooms and then save it we make use of the cp (copy) command to create the rest of the template rooms:
cp cp cp cp street_03.c street_03.c street_03.c street_03.c market_southwest.c market_southeast.c market_northwest.c market_northeast.c

Now when you ls (list) the directory, you'll find it full of your skeleton rooms.
1 market_northeast.c 1 street_03.c 1 street_02.c 1 market_southeast.c 1 market_northwest.c 3 street_01.c 1 market_southwest.c

We haven't added the exits to these new rooms, but that's okay we need to talk a little bit about exits before we leap into that part.

48

Epitaph Online

http://drakkos.co.uk

Exits and Maintainability


If you've been looking through the 'here's one that I made earlier' versions of these files in the /d/support/deadville directory, you'll see that it's broken up by chapter. Each directory contains the code as it stands by the end of the appropriate chapter. I'm not writing this code every time I'm copying it across from the last version. However, the exits from my_first_room don't work after moving it into the directory of my_first_area every time a move is made, you end up in the room for my_first_room. You can change the filenames by hand, and that's not too bad for two rooms. We've got seven now, and four more to come it would be a nightmare to have to change all of those paths by hand, for every chapter of this book. It's too much to ask of anyone, especially me because I really don't want to have to do it. The same thing is true with your code sometimes things get moved around as directories are reshuffled, cities are redesigned, and generally treated with an incredible amount of casual disrespect. Even the Gods don't pick up cities and rehouse them with the indifference we do. Changing every exit in a large city by hand would be an impossibly annoying task, and one you'd just need to repeat when the city moved directories in the future. It's not a sustainable approach. We have a way around that problem though we make use of what is known as a header file. Create a new file and save it as path.h (note, .h on the end rather than .c). It's going to contain one single line:
#define PATH "/w/your_name_here/deadville/rooms/"

This is a special kind of code statement in LPC. Technically it's called a preprocessor directive it's something that LPC deals with before it ever gets to your code. There is a subsystem in the driver called the preprocessor, and it's kind of a souped-up search and replace tool. For the code we have here, we have given the preprocessor a directive to define the text PATH as being another string entirely. When you create your path.h file, make sure you hit return at the end of the line. If you don't, you'll get a worrying error saying something about #pragma strict_types. If this happens, go back to your path.h file and add a return at the end. The preprocessor is essentially a sophisticated search and replace routine providing a #define statement tells the preprocessor that every time it sees the first term to replace it with the second term. Thus, any time you use the text PATH in your program, it will replace it with the text "/w/your_name_here/deadville". This happens transparently, and every time the object is loaded. You see no impact on your code.

49

Epitaph Online

http://drakkos.co.uk

Simply creating a path.h file is not enough though, we need to tell our rooms to use it. We do this by using another directive called an include directive. At the top of each of your room files, before your inherit line, add the following:
#include "path.h"

This tells the preprocessor to take the path.h file you just created, and incorporate it into your rooms as such, each of your rooms gets the define that is set. In your skeleton rooms, the code will now look like this:
#include "path.h" inherit STD_OUTSIDE; void setup() { set_short ("skeleton room"); add_property ("determinate", "a "); set_long ("This is a skeleton room.\n"); set_light (100); }

Now, let's see what impact that has on our original two rooms. Remember we have already set up their exits, like so. In street_01.c, we have:
add_exit ("northeast", "/w/your_name_here/deadville/street_02", "path");

In street_02.c, we have:
add_exit ("southwest", "/w/your_name_here/deadville/street_01", "path");

We're going to change these so that they read as follows. For street_01.c, we will have:
add_exit ("northeast", PATH + "street_02", "path");

For street_02.c, we will have:


add_exit ("southwest", PATH + "street_01", "path");

Now, update your two rooms you'll see you can move between them just as easily as you were able to do before. That's because before LPC even begins to try and load your code, the preprocessor looks through your code for any instance of PATH and replaces it with your define so what LPC sees when it comes to load your room is the following:
add_exit ("southwest", "/d/support/deadville/my_first_area/rooms/" + "street_01", "path");

LPC is perfectly capable of adding two strings together, and once it's done that it ends up with the full path to the room. The difference here though is that if you change it in your path.h file, it changes in every file that makes use of that path.h one change in the right place and an entire city can be shifted from one directory to another without anyone lifting a finger. It's very powerful, and an approach to development into which you should definitely get into the habit. 50

Epitaph Online

http://drakkos.co.uk

Check with your domain administration to see how this gets handled in your own domain. Some domains have a single .h file (such as game.h) that serves as a master file for every room. Some have a more distributed approach whereby smaller .h files are available to subsections within a development.

Our Exitses
Now, we need to go back over our skeleton rooms and add in the exits you should be able to do this quite easily by yourself now, as long as you know which rooms are heading where (and the map will tell you that). Don't add any exits for the inside rooms yet we'll get to those later. To make sure your exits look the way they are supposed to, here's the code for the exits in each of your rooms: street_01:
add_exit ("northeast", PATH + "street_02", "path");

street_02
add_exit ("northeast", PATH + "street_03", "path"); add_exit ("southwest", PATH + "street_01", "path");

street_03
add_exit ("east", PATH + "market_southwest", "path"); add_exit ("southwest", PATH + "street_02", "path");

market_northeast
add_exit ("south", PATH + "market_southeast", "path"); add_exit ("west", PATH + "market_northwest", "path");

market_northwest
add_exit ("south", PATH + "market_southwest", "path"); add_exit ("east", PATH + "market_northeast", "path");

market_southeast
add_exit ("north", PATH + "market_northeast", "path"); add_exit ("west", PATH + "market_southwest", "path");

market_southwest
add_exit ("north", PATH + "market_northwest", "path"); add_exit ("east", PATH + "market_southeast", "path"); add_exit ("west", PATH + "street_03", "path");

There's a particular convention that is followed when adding multiple exits when you walk around the game, the exits are presented to you in the order in which they are added in code. To improve consistency, try to add relevant exits in the following order: north, south, east, west, northeast, northwest, southeast, southwest, and then any other exits. No-one will shout at you if you don't do this, but it's the convention. One thing you may have noticed from other parts of the game, especially in 51

Epitaph Online

http://drakkos.co.uk

market squares, is that there are sometimes diagonal exits you can take that aren't listed if you can go north and then east to get to a particular room, why can't you go northeast? There's no reason why we should exclude this, so let's first add in the diagonal exits in the market rooms: market_northeast
add_exit ("southwest", PATH + "market_southwest", "path");

market_northwest
add_exit ("southeast", PATH + "market_southeast", "path");

market_southeast
add_exit ("northwest", PATH + "market_northwest", "path");

market_southwest
add_exit ("northeast", PATH + "market_northeast", "path");

Next, we will set them to be hidden. Why do we do this? Mainly it's to avoid cluttering up the obvious exits list - whether you want to make your exits hidden is entirely up to yourself, but there's no reason why you shouldn't know how to do it. We make use of a new piece of code called modify_exit for this modify_exit is tremendously powerful, and we'll return to it several times in the future. In each of the market rooms, we'll modify the exit of the diagonal so that it is set as 'non-obvious'. It's still there, just not listed. For example, in market_northeast we'd just the following line of code:
modify_exit ("southwest", ({"obvious", 0}));

Do the equivalent of this in all four market rooms and update you'll find the exits disappear from the list, but you're still able to take them just as before.

Chain, chain, chain...


Another thing you'll have noticed as you wander around the game is that some parts of various cities give you informs as to what's happening in another part... for example, you'll see something like
Drakkos moves southeast onto the centre of the city square.

It would be cool if our market rooms did that too and they're going to by making use of a thing called the linker. First though, we need to make sure their shorts are set properly, because that's what's used to build the message. In each of the market rooms, change the shorts as follows: market_northeast
set_short ("northwest corner of the marketplace");

52

Epitaph Online

http://drakkos.co.uk

market_northwest
set_short ("northwest corner of the marketplace");

market_southeast
set_short ("southeast corner of the marketplace");

market_southwest
set_short ("southwest corner of the marketplace");

For each of these, you should also set the determinate as 'the' instead of 'a'. In that way, people will see, for example, 'the northeast corner of the marketplace' when people move about. If it's set to 'a', then they'll see 'a northeast corner of the marketplace', which doesn't look right, not right at all! Setting the shorts properly ensures that the messages will be properly formed, but still need to tell the MUD we want it to happen. We do this using set_linker. We give the MUD each room we want to link together except for the room in which we're defining the code. For market_northeast:
set_linker ( ({ PATH + "market_northwest", PATH + "market_southwest", PATH + "market_southeast", }));

For market_northwest:
set_linker ( ({ PATH + "market_northeast", PATH + "market_southwest", PATH + "market_southeast", }) );

market_southeast:
set_linker ( ({ PATH + "market_northeast", PATH + "market_northeast", PATH + "market_southwest", }) );

And market_southwest:
set_linker ( ({ PATH + "market_northeast", PATH + "market_northeast", PATH + "market_southeast", }) );

You'll need to log on a test character to make sure that you've got this all setup properly, you can't test it from your own perspective. Create a test character, bring them into the world, and then set them as a test character using the testchar command:
testchar <testchar_name> on

Trans your testchar to your village and make them dance around a bit for you. 53

Epitaph Online

http://drakkos.co.uk

What you should see are messages along the lines of the following:
Draktest moves south into the southwest corner of the marketplace.

Provided that message looks right from every part of the marketplace, you've got it all configured properly. Well done! The linker is also used to determine what is an valid direction for things like binoculars and hunting rifles - it's not just a presentational thing, it's a neccessary component of making sure that these systems work correctly in your areas.

What is all that crazy STD_OUTSIDE stuff, anyway?


When we tell the MUD we want to inherit something, we basically say 'Hey, I don't want to write all the code for this myself. Give me some code that already exists'. In the case of Epitaph, all our outside rooms are based on, at some point in their code, an object that has been #defined as STD_OUTSIDE. 'But Drakkos', I hear you ask, 'I didn't define that!' That's exactly right, and well done for noticing. Every object on Epitaph gets a 'free' file #included. You don't get a say in this, it just happens that file is /include/all_inc.h. One of the lines in that file is:
#include <mudlib.h>

That's the file that holds all of the standard defines for inherits. One of those is:
#define STD_OUTSIDE MUDLIB_INHERITS + "outside"

MUDLIB_INHERITS is another define, which is in itself based on another define. If you want to find what file a define in mudlib.h points to, you can do this:
> exec return STD_OUTSIDE Returns: "/mudlib/inherits/outside"

So, in our code it is effectively the same as if we had the following line of code:
inherit "/mudlib/inherits/outside";

In fact, if you change all your inherits to that line, it will work exactly the same way as it did before. Sadly, you won't know which of these you are supposed to use at particular times yet, but that will come as we progress through the material. We do it this way because it makes our mudlib easier to move around as needed if we need to change the directory all of our inherits are in, we don't need to manually change every inherit in every file we just change it once in mudlib.h and away we go. That is a huge deal, and not just because I once had to do exactly that go over EVERY SINGLE FILE and change the paths. Oh god, the horror. 54

Epitaph Online

http://drakkos.co.uk

Conclusion
Having a skeleton of an area is a great way to give you a perspective of how it all fits together, as well as a solid idea of how much work you have to do. There's nothing wrong with the 'write one room and link it in' approach, but you get a much better idea of the bigger picture by architecting it all together and just seeing how it feels. That way, if you think that the layout needs to change, you can do it before you've hooked too much of it together. It's just a nice way to get some perspective. My personal preference is to do this and then watch as the area starts to evolve in line with my development. It's very nice to be able to see an area taking shape before you. When we did the city of Dunglen, the outer core of the city was all created and linked together before anything had really been done, and it was great to watch it slowly constructed around me as various creators went about their business. You should however find an approach that works best for you. Your mileage may vary, as they say.

55

Epitaph Online

http://drakkos.co.uk

Building the Perfect Beast


Introduction
It's awfully lonely in our little village, isn't it? I think it is, anyway and surely, like I, you crave some kind of company on your quest to become a fully capable Epitaph creator. In this chapter we're going to build the first inhabitant of our area, and provide him with equipment, responses, and chats. He's going to be our little living doll, for us to taunt and make dance for our sport. In the process, we'll have to look at some new syntax and introduce a new concept of LPC programming, that of the variable. We've come quite far without actually talking abut variables, but you've been using them all along yes, that's right! That's the SHOCKING TWIST of this chapter - variables are the Kaiser Soze of LPC programming!

A First NPC
Let's start off by creating a new subdir in our Deadville directory this one will be called chars:
mkdir /w/your_name_here/deadville/chars/

It is in this directory that we will include the code that creates any NPCs in our area. Keeping a clean divide between rooms, NPCs and items is an important part in ensuring it's easy to integrate your code into a larger domain plan. This is going to cause some complications for our path.h file, which we'll talk about a bit later there is no problem so great that we cannot solve together, though! Anyway, creating an NPC follows a very similar process to creating a room what changes is the inherit we use, and the specific code that goes into the setup of the object. Consider the following for a basic template save it into chars with the filename captain_beefy.c.
#include <npc.h> inherit STD_MONSTER; void setup() { set_name ("beefy"); set_short ("Captain Beefy"); add_property ("determinate", ""); add_property ("unique", 1); add_adjective ("captain"); add_alias ("captain"); set_gender (1);

56

Epitaph Online

http://drakkos.co.uk

set_long ("This is Captain Beefy, a former officer of Her Majesty's " "Secret Service. Once he was a pretty impressive guy, with a large " "string of accomplishments to his name. Now, he's just another " "survivor in the grim darkness of the LPC apocalypse.\n"); basic_setup ("human", "survivor", MONSTER_LEVEL_NORMAL); }

Let's go through that line by line, as we did for our first room:
// npc.h is a mudlib include file it works much like your path.h, // but contains definitions used by all NPCs. #include <npc.h> // If we want to create a basic NPC, this is the file we inherit. inherit STD_MONSTER; void setup() { // The name of the NPC is how it is identified by the MUD's matching // system. This should be one single word - it should usually too be // the last word in the short, for simplicity's sake. set_name ("beefy"); // This is what players see when they encounter the NPC. set_short ("Captain Beefy"); // This is what gets prepended to the short of the NPC. Since Captain // Beefy is a unique person, he gets his determinate set to empty. If // he was one of a number of clones (for example, a beggar), then // the determinate could be set to 'a', or even 'the'. add_property ("determinate", ""); // Captain Beefy is a unique NPC - there should only ever be one of him. // This code doesn't ensure that, but it does mean when he's killed he'll // trigger a death inform. add_property ("unique", 1); // The name is used to match an NPC, but we also need to set valid // adjectives. If we don't include this, our NPC can be referred to as // 'beefy', but not 'captain beefy'. We want both to be valid. add_adjective ("captain"); // We also want people to be able to refer to him just as 'captain', so // we add an alias for him. add_alias ("captain"); // He needs a gender, because he's a he. Setting the gender to 1 makes // him male. 2 makes him female. Everything else makes him an 'it'. set_gender (1); // The long is what players see when they 'look' at him. set_long ("This is Captain Beefy, a former officer of Her Majesty's " "Secret Service. Once he was a pretty impressive guy, with a large " "string of accomplishments to his name. Now, he's just another " "survivor in the grim darkness of the LPC apocalypse.\n"); // You need this in the code, or try as you might he won't clone when you // want him to. The first piece of information we provide is his race. // The second is his guild. The third is his 'monster level'. basic_setup ("human", "survivor", MONSTER_LEVEL_NORMAL); }

57

Epitaph Online

http://drakkos.co.uk

So, update your code, and clone him he'll appear in the same room as you if all has gone to plan:
You can hear: Nothing. In the distance you can see: Nothing. This is a simple road, leading to a simple village. There is very little of an excitement about it, except that it represents your first steps to becoming a creator! It is a very cold winter afternoon with torrents of stinging sleet, medium cloud cover and almost no wind. There are two obvious exits: northeast (clear) and southwest (clear). Captain Beefy is standing here.

We can interact with him in the same way we can with any object in the game, but there's not much point. He doesn't do anything interesting at all. He just stands there looking gormless. But he loads! Chant it like a mantra, 'But he loads!'. That's always the first promising step you take. After that, the rest is inevitable.

Breathing Life into the Lifeless


First of all, let's make him emit some chats. Much like with our rooms, we can make our NPCs a little more interesting by adding chats. Moreover, we can make these chats different depending on whether the NPC is being attacked or not. The mechanisms by which we do this are identical in terms of syntax and meaning, but they're quite different from how it is handled in rooms. The code we need is called load_chat for normal chats, and load_a_chat for attack chat. Let's add some chats into our NPC, and talk about what the code means:
load_chat(10, ({ 2, "' Please don't hurt me.", 1, "@cower", 1, "' Here come the drums!", 1, ": stares into space.", }))

This sets up the random chats for the NPC. The first number dictates how often a random chat is made. Every two seconds, (a period of time known as a heartbeat) the mud rolls a one-thousand sided dice (metaphorically), and if the result is lower than the number set here, the NPC will make a chat. It's not exactly fine-grained control, but control it is. Each of the chats has two parts - a weighting, and a command string. The weighting is the relative chance a chat will be selected. The string is the command that will be sent for the NPC to perform. If you want the NPC to say something, then start the command with an apostrophe. If you want the NPC to emote, then start the command with a semi-colon. If you want the NPC to perform a soul, start the command with an at symbol. To understand the way the weightings work, think of it as a roulette wheel add up the weightings of all the chats, and it'll come to 5. When the MUD determines 58

Epitaph Online

http://drakkos.co.uk

the NPC should make a chat, there is a 2/5 chance it'll be the first chat, and a 1/5 chance it'll be each of the others. As usual, there is more that you can do with load_chat than we've covered here we'll get to some advanced stuff later in the tutorials. One proviso here is that if you want your NPC to actually say things, it's going to need a language and a nationality. We can provide that by using the setup_nationality method providing in an appropriate combination of nationality and region. Your domain leader will be able to tell you which of these is appropriate for your domain, but for demonstration purposes we'll make Captain Beefy a Scotsman. Put it after basic_setup in your code:
setup_nationality(MUDLIB_NATIONALITIES + "british", "scotland");

To add the attack chats, it works the same way we just use load_a_chat instead:
load_a_chat(10, ({ 1, "' Oh please, not again!", 1, "' I don't get medical insurance!", 1, "@weep", }));

When you make changes to Beefy, you'll need to dest him before you update and reclone. Once you've added the chats, go on take a swing at him! You may find he doesn't swing back if you're invisible, that'll be why. But you should find he pleads pitifully before your Awesome Might:
You punch at Captain Beefy. Captain Beefy exclaims: Oh please, not again!

Poor fellow. He doesn't even know what Fresh Horrors we have in store for him to come. So, that's fine for making him a little more interesting however, we also want to be able to make him reply when we say things to him. Let's add some of that now using add_response. The syntax for this is similar to that for add_item:
add_response (({"murder"}), "' Please don't kill me!");

This sets up a response to a message that originates with the command 'ask'. It will match on any string containing either the words 'hello', 'hi', or 'hiya', provided it originates from an ask command. Beefy's response to this will be to say "Er, hello. Please don't kill me.", like so:
> ask beefy about murder You ask Captain Beefy a question about murder. > Captain Beefy exclaims: Please don't kill me!

Let's add in a second response, since he's already opened us up to the possibility of killing him:
add_response ("kill", "' No, please! God, please don't kill me!");

59

Epitaph Online

http://drakkos.co.uk

We can also make him respond to the 'greet' command by adding in a greet response:
set_greet_response ("' Oh, I hope you are not here to kill me. "hello."); I mean, "

This has the following happy outcome:


> greet beefy You greet Captain Beefy. > Captain Beefy says: Oh, I hope you are not hear to kill me. I mean, hello.

In a similar vein, if we want to make him have a response whenever we ask him about something he doesn't have a specific response for, we can use set_unknown_response:
set_unknown_response ("' I don't know anything about that.");

And then:
> ask beefy about beef You ask Captain Beefy a question about beef. > Captain Beefy says: I don't know anything about that.

While add_response doesn't give especially fine grained control over interactions with NPCs, the nature of the system (for example, the MXP links or coloured text it will provide for conversational keywords) ensures that NPCs are appropriately interactive for players.

Cover Yourself Up, You'll Catch a Cold


Captain Beefy will now chat with us a bit, but he's still horribly naked. That's embarrassing for everyone concerned, so let's dress him up a bit, like the little doll he is. We have two mechanisms for doing this. If we are completely unconcerned about the outfit that Beefy should wear, and instead are more interested in simply ensuring that we can't see his reproductive business, then we simply say dress_me within the setup for the NPC:
dress_me();

You get some control over how the dressing should work by passing parameters (more on this later), but by default it will dress him up as a survivor from the appropriate game region (based on his nationality). Once we update him and reclone him, we'll see something like:
This is Captain Beefy, a former officer of Her Majesty's Secret Service. Once he was a pretty impressive guy, with a large string of accomplishments to his name. Now, he's just another survivor in the grim darkness of the LPC apocalypse. He is in good shape. He is standing.

60

Epitaph Online

http://drakkos.co.uk

Holding : a machete (left hand). Wearing : a pair of white cotton sports socks, a pair of blue denim jeans and an orange football scarf.

He'll get a different outfit each time, and it'll always look this ramshackle (here, because he's a survivor of a zombie apocalypse and is thus making do the best he can), but it will be as far as is possible gender appropriate. It's a quick and easy way when you don't really want or need to worry about the equipment with which an NPC is provided.

Request Item
If we don't want some random clothes, or if we want to supplement his inventory with something more specific, we have available another piece of code called request_item. This lets us conjour up specific items from the armoury in the same way that the creator request command permits. Let's give him a bra. Just because we can.
request_item ("plain bra", 100); init_equip();

The first part of the request_item line is the item we want from the armoury. The second is the condition that it should have. The init_equip line means 'now wear those things we have given you'. If he can't wear the clothes we give him (because he is already wearing clothes of that kind), they'll just hang around in his inventory. You can use both of these techniques if you want (dress an NPC randomly and then give him or her specific items), or you can use one or the other exclusively. Either approach is appropriate it just depends on your specific requirements.

Challenges
By default, Beefy has only the standard five levels of each skill that everyone gets. If you want him to be a little more challenging, then you need to add challenge profiles to him. On Epitaph, we don't set an NPC's skill levels directly. Instead, we use challenge profiles - these let us say 'this NPC should be this good at this particular thing'. A profile consists of several skills, each of which gets layered onto the NPC. These are defined in challenges.h (so you will need to #include that), and are used like so:
set_challenge_profiles (([ PROFILE_SHARP_WEAPONS : CHALLENGE_INCREDIBLY_EASY, PROFILE_PIERCE_WEAPONS : CHALLENGE_INCREDIBLY_EASY, PROFILE_BLUNT_WEAPONS : CHALLENGE_INCREDIBLY_EASY, PROFILE_UNARMED : CHALLENGE_INCREDIBLY_EASYd, ]));

61

Epitaph Online

http://drakkos.co.uk

This handles everything from setting up skill levels, adjusting stats where appropriate, and adding neccessary commands. This system permits us to balance NPCs against each other and against the various tiers of difficulty we already have in the game. Additionally, these challenge profiles are used when considering an NPC, permitting players to get a fine-grained breakdown of an NPCs capabilities:
> consider beefy As best as you can tell, Captain Beefy is extremely bad at fighting unarmed, extremely bad at combat with pierce weapons, extremely bad at combat with sharp weapons and extremely bad at combat with blunt weapons. You consider Captain Beefy.

If you don't really want this level of fine grained control, and you just want to be able say 'he should be pretty good as a soldier', then you can use one of the skillset packages, like so:
set_challenge_profiles (([ "warrior" : CHALLENGE_AVERAGE, ]));

Skillset packages are defined in /data/skillsets/, and essentially work like a package of challenge profiles that get assigned to an NPC. The one for warriors looks, in part, like this:
::item "warrior":: ::->name:: "warrior" ::->other_skillsets:: ({ PROFILE_SHARP_WEAPONS, PROFILE_FIREARMS, PROFILE_PIERCE_WEAPONS, PROFILE_BLUNT_WEAPONS, PROFILE_UNARMED, PROFILE_OBSERVANT }) ::->stats:: ([ "strength" : 1, "dexterity" : 1, "constitution" : 1, ])

This system means you don't need to worry about the book-keeping of NPC skill levels the various game handlers will configure the numbers for you.

Chatting Away
There's one thing left for us to talk about in this chapter, and it's the special codes that can be used within load_chat and load_a_chat to make our NPCs more involved in the world around them. We can build special strings that get interpreted by the MUD and turned into meaningful output based on the things around our NPC. It's easier to see what that means in practise than describe it, so let's add a load_chat to Captain Beefy: 62

Epitaph Online

http://drakkos.co.uk

load_chat(10, ({ 2, "' Please don't hurt me.", 1, "@cower", 1, "' Here come the drums!", 1, ": stares into space.", 1, "' Oh, hello there $lcname$.", }));

See the last chat there? The weird looking code is interpreted by the MUD to take the form of the name of a living object in the NPC's inventory. The first letter (l) defines what object will be the target of the chat, and the string that follows (cname) defines what is used to build the rest of the string. When the chat is selected, a random object is picked from the specified set, and then the requested query method is called on it and substituted for the special code we provide. The letters we have available for choosing the set of objects is as follows: Letter m l a o i Object The NPC itself. A random living object in the NPC's environment. A random attacker in the NPC's environment. A random non living object in the NPC's environment. A random item in the NPC's inventory.

Following the initial letter comes the type of information being requested. This gets called on the random object that is selected when the chat triggers. Some of these are more useful than others, but you may find cause to use even the more specialised ones on occasion: Code name cname gender poss pronoun ashort possshort theshort oneshort Request The file name of the NPC - used for targeting souls The capitalised name of the object The gender string of the object The possessive string The pronoun of the selected object The a_short() of the object The poss_short of the object The the_short of the object The one_short of the object

As an example, we could get the short of an object in the NPC's inventory with $itheshort$. We could get the name of a random attacker in load_a_chat with $acname$. Unfortunately, we get no fine-grained control over the object selected if we choose the item set of objects, we can't further specialise it, so a chat like the following will not be appropriate:
"' I am going to stab you in the eyes with $ipossshort$!"

63

Epitaph Online

http://drakkos.co.uk

It'll parse properly, but he'll end up saying things like 'I am going to stab you in the eyes with my floppy clown shoes!' which, while frightening, is not really sensible. Despite the limitations, combining these codes will allow for you to make your NPC chats more dynamic and responsive to the context in which it finds itself.

64

Epitaph Online

http://drakkos.co.uk

Conclusion
We've come quite far in this chapter, having created an interactive NPC who is dressed in a fashion that does not offend our Victorian sense of decency. However, we need to manually clone Captain Beefy into our village each time we want him there. In the next chapter we'll look at how we can get that to happen automatically without our intervention. At the moment we're making use of only those items that the armoury can provide, but we'll also look at ways in which we can write our own objects and make them available to our NPCs and our rooms.

65

Epitaph Online

http://drakkos.co.uk

Hooking Up
Introduction
So, we now have a set of rooms, and we have an NPC. They're simple, but they work. It's not appropriate though that you have to clone the NPC directly into your room it should happen automatically, and we're going to talk about how that works in this chapter. We're also going to resolve the path.h problem that we introduced in the last chapter by looking at relative and absolute directory referencing. So buckle up, time to take LPC out for another spin!

The Path.h Problem


Look at where we've got our path.h file stored it's in our rooms directory. Although we haven't needed to refer to it in the NPC we created, we should still be able to get access to it without too much complication... the idea of a header file is that it's shared between all relevant objects, after all. We have a couple of possibilities. One is to copy the path.h file into each directory that we are likely to need it. This is a bad solution because it reintroduces the problem it was designed to fix we need to change the defines in multiple places if we want to shuffle things around. That's not ideal we want to be able to change things in one place and have it reflected in all appropriate locations. Our second possibility is to make everything use the same path.h file that's a better solution, but it's going to need us to change all the references to the path.h in all our code. We'll need to put it in a central location, and then have all of the files #include it from there. That's not a bad solution - there are plenty of .h files in /include/ that work this way, but you need special file access to put files in there, and comparatively few creators have that access. We'd need to store it then in a set directory in our /w/ drive. We can do better than that though after all, if we move our code from /w/ to /d/, we're going to have to remember to move the .h file along with it, and then change all the references to the path.h file to reflect its new location. Ideally it should just be a case of copying a directory across and having done with it. How about this though we keep a path.h in each subdirectory, but we have that path.h in itself include a path.h in a higher level directory? That way, provided the basic structure of the directory remains intact, we have a chain of path files that define all the values we need. That may sound confusing, but let's see it in practice it should become a bit clearer with an example. 66

Epitaph Online

http://drakkos.co.uk

Let's start with a new path.h file in our base Deadville directory:
#define #define #define PATH ROOMS CHARS "/w/your_name_here/deadville/" PATH + "rooms/" PATH + "chars/"

In each of your subdirs, you add a further path.h file that includes the path.h from the higher level directory. We can do that using the .. symbol to to represent the higher level directory in the #include. The double period symbol has a special meaning in a #include - it means 'go to the directory one up from the current directory':
#include "../path.h"

Now, we are going to have to change our room code a bit because we're making a distinction between ROOMS and CHARS. Luckily we don't need to do that by hand, we can use the sar command to do a search and replace. I shall warn you in advance though, be VERY CAREFUL when using this command. One creator, who shall remain nameless (Terano@Discworld) once mistakenly changed every instance of the letter a in all the priest rituals to the letter e. While tremendously funny (to everyone else), it was hugely problematic for him to fix. Anyway, the sar command needs three pieces of information the string of text you want to replace (surrounded in exclamation marks), the string of text you want to replace it with (again, surrounded in exclamation marks), and the files you want the text replaced in. Let's run that puppy over our code. First, you change your your current directory to the rooms subdirectory, and then:
sar !PATH! !ROOMS! *.c

Upon uttering this mystical incantation, you'll find all of your rooms now reflect the New Regime. Update them all (you can do this using update *.c), and you'll find everything is Hunky Dory. If it's not, remember what we discussed about path.h files in a previous chapter if they don't work properly, make sure there's a carriage return at the last line. Sometimes LPC chokes on a file if that's not the case. With regards to sar, please remember use this command with caution. It's incredibly easy to do some really quite impressive damage to your hard work with only a few misplaced keystrokes. You Have Been Warned!

67

Epitaph Online

http://drakkos.co.uk

Sorted!
Right, now we've gotten that out of the way, let's look at how we can make Captain Beefy appear in our rooms. We're going to pick a room for him (we'll choose market_northwest) and talk about how it works. Cloning a unique NPC into a room actually involves only four pieces of information the path to the NPC, the message to present when the NPC appears in a room, and the message to present if the NPC has to be moved from another room. The code that does this is called add_unique_spawn. It goes into the setup of a room, just like most of the other things we've spoken about, and in its simplest form it will look something like this:
add_unique_spawn (CHARS + "captain_beefy", "$N appear$s with a pop.", "$N disappear$s with a pop.");

The move messages here get handled by the MUD so that they look sensible for everyone viewing them - $N becomes the name of the NPC, and the $s code means 'add an s to this word where it is appropriate'. If you were Captain Beefy, you'd see 'you appear with a pop', whereas everyone else in the room would see 'Captain Beefy appears with a pop'. The MUD's message handling routines are pretty cool like that. Update your room, and lo and behold Captain Beefy appears:
Captain Beefy appears with a pop.

For now, that's all we need to do add_unique_spawn lets us do a lot more than I am letting on here, but that's functionality you'll come to uncover as the need arises.

Reset Your Phasers for Stun


For the next part of this material, we need to talk about a new kind of programming concept - the function. A function is essentially a little selfcontained parcel of code that gets triggered upon request - sometimes on our request, sometimes on the request of another object in the MUD. We don't need to worry too much about it just now, we just need to know that's what we're about to do. Functions have many different names - they most common one you'll also see in this material is the word 'method'. It's just another word for the same thing. There are certain functions that the MUD executes on all Epitaph objects at predetermined intervals. One of these we're familiar with the code that belongs to setup gets executed when we update our objects. There's another function that gets called at regular intervals, and that's the reset function. The reset function is called on rooms when a room is loaded (and it's called just after setup), and also at regular intervals (of around thirty minutes or so) on all currently loaded rooms. Just for the hell of it, we're going to make use of this to, every so often, make 68

Epitaph Online

http://drakkos.co.uk

Beefy have a heart attack and die. Why? No reason, other than sadism and the fact it is instructional. To do this, we're going to add a new function to market_northwest, like so:
void reset() { ::reset(); }

This function exists outside of any code you've already got. So within your room, it will look like this:
void setup() { // All your current code } void reset() { ::reset(); }

Yikes! What does that code inside the reset method mean? Well, you know how at the top of your code you have an inherit line? Well, that STD_OUTSIDE object has a reset method of its own as part of its code. The ::reset() line says to the MUD: 'Oh, call reset in the object from which I'm inheriting, please'. Don't worry too much about it just now, just make sure it's there. Next, we need to find Beefy in our function. He's in our room, but we don't have any way to refer to him in code at the moment. First, we need a variable that is going to hold him when we find him:
object beefy;

This line of code comes before the ::reset() in your reset function all variables have to be declared at the top of a function, before any other code. That's just a thing LPC insists upon. Now we can start building our hateful heart-attack code, which we do after the ::reset() line. Our process is as follows: Find Beefy Check to see if he's in our current room If he's there and we roll under ten on a d100, then kill him.

Poor Beefy! Little did he know that zombies were only the start of his problems! Before we can manipulate Beefy, we need to find his object reference, which is the unique identifier that the MUD uses to reference him. Every object on the MUD has an object reference including you! You can find yours by doing something like:
pathof <your_name_here>

69

Epitaph Online

http://drakkos.co.uk

The code you get back is your object reference. Beefy has one of those too, which you can also find by using pathof:
> pathof beefy Path of Captain Beefy in northeast corner of the marketplace: /w/<your_name_here>/deadville/chars/captain_beefy

The object reference of objects change on a regular basis when you log off and log back on, your object reference will change. If we want to find what the current reference of Beefy is, then we can use the find_object function:
beefy = find_object (CHARS + "captain_beefy");

Now, our variable 'beefy' contains, in a real sense, our sweet, innocent Captain Beefy. And we can do stuff to him. Before we can do that though, we need to explore the first of our programming structures the if statement. Oh man, life is about to get PRETTY EXCITING for you!

If at First You Don't Succeed


The if statement is the first programming structure we're going to learn how to use. It allows you to set a course of action that is contingent on some preset factor. The basic structure is as follows:
if (some_condition) { some_code; }

The condition is the most important part of this it's what determines whether the code between the braces is going to be executed. A condition in LPC is defined as any comparison between two values in which the comparison may be true or false. If the comparison is true, then the code between the braces is executed. If the comparison is false, LPC skips over the code in the braces and instead continues with the next line of code after the if statement. The type of comparison depends on which of the comparison operators are used... these go between the two values to be compared, and determine the kind of comparison to be used:

70

Epitaph Online

http://drakkos.co.uk

Comparison operator == < > <= >= !=

Meaning Equivalence does the left hand side equal the right hand side? Is the left hand side less than the right hand side? Is the left hand side greater than the right hand side? Is the left hand side less than or equal to the right hand side? Is the left hand side greater than or equal to the right hand side? Does the left hand side not equal the right hand side?

Let's look at a simple example of this in practise using whole numbers (the int variable type):
int num1; int num2; num1 = 10; num2 = 20; if (num1 < num2) { tell_creator ("your_name_here", "num1 is less than num2!\n"); } tell_creator ("your_name_here", "I'm out of the if!\n");

So, if the value contained in the variable num1 is less than the value contained in the variable num2, then we see the message sent to our screen. If it isn't, then we don't. In either case, we'll see the "I'm out of the if!" message. So, with the values we've given num1 and num2, our output is:
num1 is less than num2 I'm out of the if!

If we change the two variables around a bit:


num1 = 20; num2 = 10;

Then all we see is:


I'm out of the if!

An if statement by itself allows for you to set a course of action that may or may not occur. We can also combine it with an else to give two exclusive courses of action:
if (num1 < num2) { tell_creator ("your_name", "num1 is less than num2!\n"); }

71

Epitaph Online

http://drakkos.co.uk

else { tell_creator ("your_name", "num1 is greater than or equal to num2!\n"); } tell_creator ("your_name", "I'm out of the if!\n");

So now, if the condition is true, we'll see:


num1 is less than num2 I'm out of the if!

And if it's not, we'll see:


num1 is greater than or equal to num2 I'm out of the if!

We can also provide a selection of exclusive courses of action by making use of an else-if between our original if (the one that starts the structure) and the concluding else (if we want one else is always optional):
if (num1 < num2) { tell_creator ("your_name", "num1 is less than num2!\n"); } else if (num1 == num2){ tell_creator ("your_name", "num1 is equal to num2!\n"); } else { tell_creator ("your_name", "num1 is greater than num2\n"); }

We can add as many else-ifs as we like into the structure until we get the behaviour we're looking for. So, that's what an if statement looks like. Let's tie that into our reset function above. Any variable that does not have anything in will have a null value (we can use 0 to represent this in an if statement). So if we want to know if our beefy variable contains an actual Captain Beefy, ready to torture. We want to see 'if the beefy variable is not zero', which is written like this:
if (beefy != 0) { // some_code; }

We can even write this in a simpler fashion LPC lets us check to see if a variable has a value set by simply including it as its own condition in an if statement:
if (ob) { // some_code; }

So, that puts us firmly in the position of having met the first of our requirements to find our Captain exists. Now, let's look at the second requirement checking to see if he's in our current room. 72

Epitaph Online

http://drakkos.co.uk

We can do that too by putting an if statement inside our if statement this is known as nesting. To tell whether or not Captain Beefy is in the same room as the code we're working, we use the following check:
if (environment (beefy) == this_object()) { }

If both of those things are true, then we can have our code to say whether or not he should have a heart attack for that, we use the random efun.
if (beefy) { if (environment (beefy) == this_object()) { if (random (100) < 10) { beefy->do_command ("' Oh, goodness gracious me!"); beefy->do_death(); } } }

This is not ideal though in general, having too many nested structures leads to clunky, inelegant code. There are sometimes very good reasons for code to be nested, but if you don't have to do it, you shouldn't. In this case, we wouldn't have to do it if we could get one if statement to check for both things. Luckily, that's something we can indeed do!

Compound Interest
We're not restricted to having a single condition in an if statement we can link two or more together into what's called a compound conditional. To do that, we need to decide the nature of the link. Things become more complicated the more conditions that are part of a compound we can have as many as we like, but let's start out as simply as we can. Because we only want to execute the code in our if statement if both conditionals are true, we use the and compound operator. In LPC, this is represented by a double ampersand: &&. If we wanted the code to be executed if one condition or the other were true, we'd use the or operator, which is a double bar: ||. We can join our two if statements together into one Beautiful Whole using our and operator:
if (beefy && environment (beefy) == this_object()) { beefy->do_command ("' Oh, goodness gracious me!"); beefy->do_death(); }

This loses the random element, so he'll die every single time but we can either reincorporate it as part of the compound: 73

Epitaph Online

http://drakkos.co.uk

if (beefy && environment (beefy) == this_object() && random (100) < 10) { beefy->do_command ("' Oh, goodness gracious me!"); beefy->do_death(); }

Or, we can have a nested if in this case, it's okay because it actually does make the code a bit more readable and makes it obvious what's supposed to happen if Beefy is in the room:
if (beefy && environment (beefy) == this_object()) { if (random (100) < 10) { beefy->do_command ("' Oh, goodness gracious me!"); beefy->do_death(); } }

That's much neater all around. It's sometimes confusing to new coders which of the compound operators they want for a particular situations. There is a concise representation of what each of these conditions means it's called a truth table. The truth table for and is as follows: First Condition false true false true Second Condition false false true true Overall Condition false false false true

This means that if both conditions in the compound evaluate to true, only then is the overall condition that governs the if statement evaluated to true. In all other cases, it is evaluated to false. For or, the truth table looks like this: First Condition false true false true Second Condition false false true true Overall Condition false true true true

More complex conditionals can be built by linking together conditional operators. That's a discussion for a later chapter though.

74

Epitaph Online

http://drakkos.co.uk

Conclusion
We've now hooked up our NPC and our rooms and in the process incorporated a header file that spans multiple directories. Not only is our area starting to shape up in terms of the contents and the features, we're doing it in such a way as to guarantee the maintainability of the code. That's incredibly important, although I appreciate it may appear underwhelming for now. We're still not talking much about code, although you've now been introduced to the first of your programming structures the function, and the if statement. You've reached a point where you now have the capability to make objects react intelligently to the circumstances in which they find themselves that's tremendously powerful! Onwards and upwards!

75

Epitaph Online

http://drakkos.co.uk

Back to the Beginning


Introduction
In this chapter, we're going to take a further look at the code we can make use of in our rooms. Hardly any of our rooms have any descriptions, and we still need to discuss some of the cooler things that can be done with add_item, as well as the way in which we can provide more realistic descriptions by incorporating the changes between night and day. This requires us to write rooms with two sets of descriptions, but the effect is really very appealing. We're also going to make Captain Beefy wander around this fine village of ours, and add a special skill based search to one of our rooms. It's all very exciting! Touch your nose!

Captain Beefy's Return


First, we're going to make Captain Beefy wander around our village after all, it gets so lonely when we're left by ourselves. NPCs wander according to a series of move zones that are defined. They are defined firstly in themselves (to determine what zones an NPC may roam within) and secondly in the rooms (to define to which zone a room belongs). We're going to define all of Deadville as a single zone, so add this to each of your rooms, somewhere in the setup function. It doesn't especially matter where.
add_zone ("deadville");

We can add multiple zones to a room, allowing for NPCs to have shared but distinct wandering areas. Once you've added that zone to each room, we need to add the correct zones to Captain Beefy. In his setup, add the following:
add_move_zone ("deadville"); set_move_after (60, 30);

We use add_move_zone to setup which zones within which our NPC is permitted to roam. We use set_move_after to set the speed Beefy will wander the first number sets the fixed delay, and the second sets the maximum number of random seconds that is added to that delay. With that code, Beefy will wander every 60 to 89 seconds. That's enough to set Beefy wandering around our village. It's quite a hassle to manually add a zone to every room there are ways and means by which the Industrious Creator can avoid this hassle, but they're a bit too advanced for us at the moment. We shall thus simply live with the inconvenience. Introductory LPC 2 will open up new worlds of shared functionality for us. 76

Epitaph Online

http://drakkos.co.uk

The Road Less Travelled


We're going to return to street_03 here it's still set as a skeleton room and has no long description, or any items. We're going to use this blank canvas as the exploration point for some new functionality. First of all, what we've done for the items and long in street_01 isn't, strictly speaking, correct. Oh, it works and it does what we said it would, but it doesn't capture the dynamism that we normally associate with Epitaph MUD. On your travels, you have undoubtedly noticed how in many areas the room descriptions, add_items and even chats in a room are different when it's night to when it's day. All rooms on the MUD should include this basic level of responsiveness to the world. Instead of using set_long, we use a pair of related methods set_day_long and set_night_long. Functionally, they are identical to set_long except that they are time of day dependant. The MUD itself handles all the switching between the right version of the long, you just need to tell it what the long should be. Like so:
#include "path.h" inherit STD_OUTSIDE; void setup() { set_short ("skeleton room"); add_property ("determinate", "a "); set_day_long ("This path is lit by bright, beautiful daylight. " "From the sun. High above. Because it's daytime, see?.\n"); set_night_long ("It's dark here, because it's night-time. As opposed " "to day time. Do you understand what I mean?"); set_light (100); add_zone ("deadville"); add_exit ("east", ROOMS + "market_southwest", "path"); add_exit ("southwest", ROOMS + "street_02", "path"); }

Note that we don't use set_long at all we only use that for rooms in which the description does not change at all from day to night an underground passage, for example, would fit that bill. Similarly, we can add day and night items to reflect the changing situations described in our longs:
add_day_item ("bright beautiful daylight", "It illuminates the world " "around you!"); add_day_item ("sun", "It burns our eyes, precious!"); add_day_item ("daytime", "That's what it is."); add_day_item ("nighttime", "Don't worry, it'll probably be daytime "

77

Epitaph Online

http://drakkos.co.uk

"forever. No need to fret."); add_night_item ("nighttime", "It's night, alright. You can tell by all " "the dark around."); add_night_item ("dark", "You can't see the dark, because it's too dark."); add_night_item ("daytime", "Those halcyon hours are gone for good, or at " "least until the sun comes up again."); add_day_item ("sun", "There's no sun, because it's NIGHT.");

If we want things that are available for day and night, we just use a normal add_item - but only if they shouldn't change their appearance. We can also enrich our rooms with alternating day and night chats:
room_day_chat ( ({ 120, 240, ({ "The daytime is full of sunlight.", "You can see everything around you, because of the sun.", "The sun is shining in the sky.", })})); room_night_chat ( ({ 120, 240, ({ "It is pitch black. You are likely to be eaten by a grue.", "Was that a grue? It sounded like a grue.", "If that's not a grue you can hear, it might be Vashta Nerada.", })}));

Obviously these are all terrible descriptions and violate all conventions of what you should put into long descriptions, items and chats that doesn't matter for now because our focus is on function not form. Simply providing day and night descriptions goes a long way to increasing the sense of richness people experience in your areas, and you should definitely get into the habit of writing them. It adds a fair bit of extra work to room development, but the payoff is worth it. It's one of the reasons why our MUD looks so much slicker than many. Sadly, we have to wait for the hours to tick by before the MUD swaps from night to day, so let us leave our descriptions there. You can use the check command to verify that they are present, but you'll need to wait until the time of day changes before you can come back and see them in their proper context. So let's move on to a different topic while we wait for the cruel, unyielding sun to set on our development.

Bigger, Better, Faster


Earlier in these documents, I mentioned that add_item was tremendously powerful. It is it lets you do all sorts of things with your items that you may not have realized. In this section, we're going to look at some of the things that add_item lets us do. Note that we need to use a more complicated version of the add_item syntax to do all of these things rather than just giving the name of the item and its description, we need to specifically state which parts of the add_item we're setting. First of all, if an item is large and solid enough for people to sit, or stand, or kneel on we should let them do that. We do that by adding a position tag to the item, giving the string that should be appended to the position, like so: 78

Epitaph Online

http://drakkos.co.uk

add_item ("jagged rock", ({ "long", "This is a jagged rock.", "position", "on the jagged rock" }));

Add this to your street_03 room, and then update. You'll now find that you can 'sit on jagged rock' but careful, that's bound to hurt. You can lie on it, stand on it, kneel on it... the usual suspects in terms of interaction choices. If you sit on it, everyone will see something like 'Drakkos is sitting on the jagged rock.". The text you set in the add_item is what defines how that message appears. You can add interaction options to the items too for example, if I wanted to make it possible to kick the stone, I add a kick tag to the code:
add_item ("jagged rock", ({ "long", "This is a jagged rock.", "position", "on the jagged rock", "kick", "Ow! That stung!\n" }));

Note that you need to end any specific verb response you define with a newline character. There's no limit to what verbs you may include it's just a string of text that gets shown to the player when they attempt to use that verb on your item. You can provide synonyms for verbs too:
add_item ("jagged rock", ({ "long", "This is a jagged rock.", "position", "on the jagged rock", ({"kick", "punch"}), "Ow! That stung!\n", }));

Next, we're going to add a search profile to another room in the village search profiles are ways of providing a set of items that can be searched up, limited by keywords, skill checks, or availability. Whatever you like, really.

Search Profiles
So, let's add a search profile to market_southwest. The function for this is called add_search_profile, and requires four bits of information the first is a keyword, or list of keywords these are the things you'll need to search for in order to find the associated items. The second piece of information is the random categories that might be found on a search, and the third is the random items. The fourth piece of information is the number that can be found every reset. Let's start off simple let's make it so people searching a 'stall' might find a bottle of water. Our function, in setup, would look like this:
add_search_profile ("stall", 0, ({"bottle of water"}), 1);

That's it everything else gets handled for us. Provided someone searches the 79

Epitaph Online

http://drakkos.co.uk

stall, they will find a water bottle:


> search stall You start to search around. As a result of your searching, you see a plastic bottle in a corner.

That's almost what we're looking for, but 'in a corner'? That doesn't make any sense we want the bottle to be on a stall (and we won't spend any time worrying about how it got there). If we want to make an add_item a potential container for items that might be found by searching, we use add_scavenge_item rather than add_item:
add_scavenge_item ("water stall", "There is a stall here, for the " "selling of water.");

This function causes an object to be created in the room an actual piece of furniture that can be manipulated in the usual ways. This means that you don't get all the flexibility of add_item, but that's okay you get all the functionality of a piece of furniture instead. Anyway, when searching the stall, we now see:
> search stall You start to search around. You see a plastic bottle on a stall.

That's much better. Search profiles are much more powerful than this there are existing profiles that contain preset lists of items. For example, let's add in a second search profile:
add_search_profile (0, 0, "generic food", 1);

This search profile requires no keyword, and will draw on the items already set up in the "generic food" search profile, which contains all the packaged food in the game.

The Taskmapper
Now that we've added a few features here and there, we're going to go back to our rock and add a command that is a bit more interesting the ability to 'vault' the rock. We need to use a little, complicated looking bit of code here, and we won't really explain it until the intermediate LPC documentation. Adjust your rock item like this:
add_item ("jagged rock", ({ "long", "This is a jagged rock.", "position", "on the jagged rock", "vault", (: do_vault :), ({"kick", "punch"}), "Ow! That stung!\n", }));

When you update your room, you'll find that it no longer works that's because 80

Epitaph Online

http://drakkos.co.uk

what the (: do_vault :) part of the code says 'I want you to use a function to evaluate this', and your room doesn't know anything about a do_vault function. We fix this by putting underneath our inherit line the following:
string do_vault();

And underneath our setup:


string do_vault() { return "You vault the rock.\n"; }

We'll return to this topic later, but what we've done here is link up our rock, a 'vault' command, and a function that we can use to determine what our player should see when they attempt to vault it. Right now, you can vault it with no problem:
> vault rock You vault the rock.

What we're going to do is make it skill checked. In order to do that, we need to talk about Epitaph's task mapping system.

Task Mapping
Epitaph uses a system that maps task codes to skills, and uses a centralized system for setting difficulties and modifiers on tasks. This means you don't get fine grained control over the bonuses required to perform certain tasks, but from the perspective of the overall game balance of the MUD, it means it's very easy for us to adjust game difficulty across the board. In order to use a skill check, you must include two header files in your code tasks.h, and task_mapper.h:
#include <tasks.h> #include <task_mapper.h>

These make available the various defines that you need in order to do a skill check. One of these is TASK_MAPPER, which makes use of a piece of code known as a handler handlers are LPC objects that have functions designed to handle a particular task within the game. Again, this is something you don't need to worry about much for now. To see the range of tasks available in the game, you can use the taskmapper command, like so:
taskmapper list

This provides you with a description of the tasks, and the skills they use. However, when you want to use one of these tasks, you must use the defined 81

Epitaph Online

http://drakkos.co.uk

value which is set in task_mapper.h. All of the tasks are located in /include/task_mappings/, and you can quickly search through these headers using the grep command. If you wanted to find the tasks that mention vaulting, you could use:
grep vaulting /include/task_mappings/*.h

And as a result, you would get:


include/task_mappings/adventuring.h:#define TASKMAP_VAULT_BARRIER "vaulting a barrier"

It is the code in bold that you will use in your task mapping. Vaulting a barrier seems like a pretty good description for vaulting a rock, so we'll use that in our call to the task mapper. The task mapper has two versions one is a simple check, which gives either a success or a failure value. The other is a complex check which involves criticality (degrees of success). We'll use the former, leaving the latter for a later chapter of a later book (it's a good deal more complicated). Much like with when we used find_object, we need something to hold the result of our skill check, and this will be an int:
int result;

Then we do the skill check, passing in several pieces of information:


result = TASK_MAPPER->perform_task (this_player(), TASKMAP_VAULT_BARRIER, TTSM_DIFF_INCREDIBLY_EASY, 0, TM_FREE);

Yikes, that does look complicated but the good news is, it's mostly stuff we've already seen. The first part of the function call to perform_task is the object doing the task. This_player() is a special function and it gives us the object that triggered the command (which is, in this case, us). The second piece of information is the task TASKMAP_VAULT_BARRIER, as we saw above in our grep. The third part is how difficult the task is this is a define set in task_mapper.h, and TTSM_DIFF_INCREDIBLY_EASY is the easiest of tasks. We can set modifiers on the performing of a task (to represent especially favourable or unfavourable conditions), but we don't have any of those so we just pass in a 0. Finally, we have the TM type these set the frequency with which TM awards are made. Since this consumes no GP, it is a TM_FREE. What comes out of our perform_task is a number that represents the result of the skill check. It will have one of three defined values:

82

Epitaph Online

http://drakkos.co.uk

Result TASKER_AWARD

TASKER_SUCCEED TASKER_FAIL

Meaning The player passed the check and also gained a skill level and should be given an appropriate message to indicate that. The awarding of the level is handled for you. The player succeeded in performing the task. The player failed to perform the task.

We need some code in place for all of these possibilities. When we want to provide a TM message to a player, we use the taskmaster_mess function that ensures that messages honour player colour settings. Our final do_vault function will look like this:
string do_vault() { int result; int success; result = TASK_MAPPER->perform_task (this_player(), TASKMAP_VAULT_BARRIER, TTSM_DIFF_INCREDIBLY_EASY, 0, TM_FREE); if (result == TASKER_AWARD) { taskmaster_mess (this_player(), ({"You feel more vaulty!", "You feel pretty nimble!"})); success = 1; } else if (result == TASKER_SUCCEED) { success = 1; } else { success = 0; } if (!success) { return "You try to vault the rock but land flat on your face.\n"; } else { return "You vault the rock like a pro!\n"; } }

Now, when you try to vault, sometimes you will succeed and sometimes you will fail, and it will be based on your skill levels. You'll also, on occasion, get a TM for it. The task mapping system is a little complex to begin with, but it gives us a huge number of benefits. If later on, we need to change the name of our skills for example, we don't need to change every skillcheck manually we just change it in the task mapper and every piece of code uses the new skill name. Don't worry, you'll get used to it!

83

Epitaph Online

http://drakkos.co.uk

Switching Things Around


The structure we have in place here is rather clunky luckily LPC provides us with a more elegant alternative the switch statement. A switch is essentially a compact representation of a complex if, else-if, else structure. First, we choose a variable to switch on in this case, it's result. We then define a case for each of the possible alternate values the switch variable may have. The code that follows the case will be the code that is executed if the switch variable has the specified value.
result = TASK_MAPPER->perform_task (this_player(), TASKMAP_VAULT_BARRIER, TTSM_DIFF_INCREDIBLY_EASY, 0, TM_FREE); switch (result) { case TASKER_AWARD: taskmaster_mess (this_player(), ({"You feel more vaulty!", "You feel pretty nimble!"})); case TASKER_SUCCEED: success = 1; break; case TASKER_FAIL: success = 0; break; }

Switch statements are somewhat more flexible than if statements, because each case is fall-through. That means that when the MUD finds a matching case statement, it executes that statement and every statement that follows until it finds a line of code marked as break. For the above code, if the result is a TASKER_AWARD it will display the TM message, and then continue on to the next case statement (TASKER_SUCCEED). So getting an AWARD gives the TM message and sets the success variable to 1. It then stops, because it hits a break. If the result is TASKER_SUCCESD, it sets the success variable to 1 and then stops. If it's a TASKER_FAIL, then it sets the success variable to 0. We can add a general catch-all to a switch by adding a special default case. We can use this to deal with results we didn't anticipate.
switch (result) { case TASKER_AWARD: taskmaster_mess (this_player(), ({"You feel more vaulty!", "You feel pretty nimble!"})); case TASKER_SUCCEED: success = 1; break; case TASKER_FAIL: success = 0; break; default: tell_creator ("your_name", "Yeah, I don't know what happened here.\n"); break; }

84

Epitaph Online

http://drakkos.co.uk

Aside from this new structure, the code should be fairly self explanatory if the skill check succeeds, the player leaps like a gazelle. If it fail, they lose their dignity as they fall flat on their face. However, we're not done yet TM_FREE only reduces the chance of a TM, it doesn't actually remove it people can vault our rock as many times as they like to try and TM. Let's be draconian and stop that we'll make it so a rock can only be vaulted once per reset. Yes, I know that doesn't make any sense, but it's a useful conceit because it introduces the newest programming concept we need to address scope.

Scoping Things Out


So, how do we limit the number of times a rock can be vaulted? The obvious thought is to use a variable something like found, in fact. How about if we just put a check at the top to see if a 'jumped' variable has been set to 1... will that work?
string do_vault() { int result; int success; int jumped; if (jumped) { return "You can't vault the rock because it has already been vaulted.\n"; } result = TASK_MAPPER->perform_task (this_player(), TASKMAP_VAULT_BARRIER, TTSM_DIFF_INCREDIBLY_EASY, 0, TM_FREE); switch (result) { case TASKER_AWARD: taskmaster_mess (this_player(), ({"You feel more vaulty!", "You feel pretty nimble!"})); case TASKER_SUCCEED: success = 1; break; case TASKER_FAIL: success = 0; break; default: tell_creator ("your_name", "Yeah, I don't know what happened here.\n"); break; } jumped = 1; if (!success) { return "You try to vault the rock but land flat on your face.\n"; } else { return "You vault the rock like a pro!\n"; } }

85

Epitaph Online

http://drakkos.co.uk

Alas, it turns out no. The code doesn't seem to do anything. Why is that? The answer lies in a programming concept called scope. Every variable that is created takes up memory on the computer in which the program is running. This is true regardless of whether the code is on a MUD or on your own computer. In order to ensure that this memory is made available when you are finished with it, the computer frees up the memory it has allocated once the variable falls out of scope. Variables that are defined within a function are called local variables, and exist only as long as that function is executing. Once your do_vault function has finished executing, the memory location occupied by the found variable is released. Then, when it is vaulted again, a new variable is setup and starts with the value 0 until it is again set by later code. The scope of the variable is the function in which it is defined. That also means that you cannot make use of that variable in other functions. We can move a variable declaration to the start of the object itself, after the inherit and before the setup:
inherit STD_OUTSIDE; int jumped; string do_vault();

This increases the scope of the variable to be class-wide. It is available to all functions, and persists as long as the object is loaded. Every function can change the state of the variable, but that value the variable gets persists as long as the object is loaded. Sounds like just what we need! Update your room and vault the rock You'll get to do it once and then you'll be told you can't do it again. That's a win! The last part of getting this working properly is to reset the value of found every time reset is called. That bit, at least, is easy:
void reset() { ::reset(); jumped = 0; }

You can test this quite easily update, search the rock, search it again and get the 'already searched' message. Then, call reset on the room manually:
call reset() here

Vault once more, and you'll see you're permitted to do it again, as if another reset period had just passed. Pretty neat, huh?

86

Epitaph Online

http://drakkos.co.uk

Conclusion
We're starting to pick up speed in our discussion of LPC in this chapter we've learned about movement zones, the task mapper, search profiles, switch statements and variable scope. That's a lot to digest, and you may want to step away from the tutorials at this point to allow the information to sink it. It's a good idea to practice with all of this try setting up other items you can manipulate with functions in other rooms. Practise is the way to gain understanding, after all. You should be feeling quite proud of yourself at this point you're starting to add some fairly sophisticated behaviour to your rooms, and your capabilities are only going to grow as we continue.

87

Epitaph Online

http://drakkos.co.uk

Now That We're an Item


Introduction
So, we've got an NPC, and we've got some rooms the next thing we need to learn how to develop is an item. This is somewhat more complicated than developing either of the others because of the sheer variety of items that can exist clothes, weapons, armours, backpacks all of them are created using different inherits and code. There are some commonalities to be sure, but they each have their own little quirks that you need to learn. More than this, there are two entirely different ways of creating items one way is a variation of what we've done before we write an object, and clone it when we need it. The other way is using the MUD's virtual item system. We'll discuss both of these in the course of this document.

Virtually Self-Explanatory
Every object that is loaded on the MUD takes up memory on the system because there are So Many Items carried by So Many NPCs and Players, there's a huge performance gain to be had by reducing the memory required for these items. The Virtual Item system was introduced to reduce the memory load on our poor little server. Remember how we talked briefly about the idea of a master object from which we create clones? Every .c file that is loaded on the MUD is a master object, and a master object comes with a memory burden. Virtual objects are just clones of an already existing master object, with all the functions we'd normally associate with setup (such as setting the name, short, and so on) are handled by the MUD as a series of calls. That may sound confusing, so I'll give an actual example of what this does when we've discussed our first virtual object. Virtual object code files don't look like normal code files they have their own syntax, and that can be quite confusing. They also have an extension other than .c, and the extension tells the MUD what kind of object we're working with. The basic extensions you'll be working with are as follows:

88

Epitaph Online

http://drakkos.co.uk

Extension .clo .arm .jew .wep .ran .food .sca

Type of Item Clothing Armour Jewellery Meele Weapons Ranged weapons Food Scabbards

In Game Directory /items/clothes/ /items/armours/ /items/jewellery/ /items/weapons/ /items/weapons/ranged/ /items/food/ /items/scabbards/

There are more of these, but we're only going to discuss clothing in this section. You should browse the indicated directories for examples of other kinds of virtual objects. Okay, let's start by creating a new directory in our Deadville directory this one will be called items.
mkdir /w/your_name_here/deadville/items/

And we'll need to add a new entry to our base path.h file in /w/your_name_here/deadville/:
#define ITEMS PATH + "items/"

Now, create this file in your new directory, under the name beefy_boots.clo:
::Name::"boots" ::Short::"pair of beefy leather boots" ::Adjective::({ "pair", "of", "beefy" }) ::Main_plural::"pairs of beef leather boots" ::Plural::"boots" ::Plural Adjective:: "pairs" ::Long::"This is a pair of extremely beefy leather boots. "need to be very beefy indeed to wear these!\n" ::Material:: (["leather" : 1]) ::Type::"boot"

A person would "

That, my young friends, is a virtual object. Although it looks entirely different from the LPC code with which you are slowly becoming accustomed, you should be able to see commonalities. Virtual files come as a list of settings, with the values those settings are to have. We update these virtual files in the same way as we do normal files, and we likewise clone them in the same way clone a pair of them into your inventory once you've updated them. From the perspective of the person who has them in their inventory, they are indistinguishable from normal LPC objects. That's because that's exactly what they are they're just written in a different way, and the MUD creates them in a different, more efficient way. When you tell the MUD to clone a virtual item, it is the extension of the file that tells it what base object it needs to make a clone of in the case of clothing, it's /mudlib/inherits/clothing.c. It just takes a copy of this object, which has all the functionality but none of the configuration details, and it takes your virtual file 89

Epitaph Online

http://drakkos.co.uk

as a template for what it should do with it. It the goes over each of the lines in the virtual file you gave it. The presence of the double colons gives a set pattern for the MUD to parse It knows that the whatever comes between the first set of double colons and the second set is the name of the setting it needs to change, and whatever comes after the second set of double colons is the value that setting should have. When it encounters the setting marked name, it knows that it translates that into calling set_name on the object it has cloned. The value for the set_name is what follows the second set of double colons. Likewise, when it gets to the setting short, it knows to call set_short. Don't copy the next bit of code into your project, it's for explanation only. Essentially cloning this virtual clothing file is the same thing to you doing the following:
object ob = clone_object ("/obj/clothing"); ob->set_name ("boots"); ob->set_short ("pair of beefy leather boots"); ob->add_adjective (({"pair", "of", "beefy"})); ob->set_main_plural ("pairs of beefy leather boots"); ob->add_plural ("boots"); ob->add_plural_adjective ("pairs"); ob->set_long ("This is a pair of extremely beefy leather boots. "person would need to be very beefy indeed to wear these!\n" ob->set_material ((["leather": 1])); ob->set_type ("boot");

A "

It probably won't be obvious at this point why this is a good way to do things trust me when I say though it saves on the memory the MUD uses, and that's a very good thing. The MUD routinely sits at around one hundred and fifty megabytes of memory usage, and it would be a great deal higher still if it weren't for systems like this to keep the requirements low. Virtual files are perfect for providing simple behaviour, but they do not offer a facility for more complex behaviour. Essentially anything that involves you adding commands, special defences, or general 'less than usual' functionality. For that, we must rely on a standard .c file. We'll see that in practise when we progress onto Intermediate LPC, later in this book.

Beefy's Boots
Now that we have a pair of delicious beefy boots for our NPC, let's give them to him! Sadly, we can't do this through request_item. Not yet. Request_item works through a handler called the armoury, and the armoury works only on 'live' code. In general we don't want personal creator code (residing in /w/) to be handled through the armoury. The armoury makes a list (and checks it twice) of all the items in the in game directories (such as /items/clothes, 90

Epitaph Online

http://drakkos.co.uk

/items/armour, and so on) and then a list of all the items available to the domain items directories (/d/game/items and so forth). What it doesn't do is make the code in your home directory available, or code that is in non-supported directories (all domain objects have to be in /d/domain/items or a subdirectory, for example). We're thus going to have to handle the provision of a pair of boots manually. First, we make ourselves a container for the boots at the top of Beefy's setup:
object boots;

Then we clone the boots using the ITEMS #define we added earlier. First though we'll need to add a #include to our path.h at the top of his file, since we currently don't have one.
boots = clone_object (ITEMS + "beefy_boots.clo"); boots->move (this_object());

Dest and update Beefy and the room from which he is cloned, and you'll find he's now wearing the lovely boots we created for him!

Bling
Let's have a look at a second kind of item created using the virtual object notation we're going to give Beefy a ring that was given to him by his Dearly Departed wife. Any player who kills him for his jewellery will thus feel like a Real Bastard. Jewellery is created according to the same basic system, although the specifics of the setup are slightly different:
::Name::"ring" ::Short::"beefy diamond ring" ::Adjective::({"beefy", "diamond"}) ::Plural::"jewellery" ::Alias:: "jewellery" ::long::"This is a beautiful golden ring set with a gleaming diamond. "It smells vaguely of beef.\n" ::Type::"ring" ::Material:: (["gold" : 1])

"

Save this one as beefy_ring.jew. Most of this should be fairly obvious by now it shares most of the settings with clothing. How do people know this is a ring given to him by his dead wife? Well, let's add an engraving to it so that people can read the loving message she left for him. We can do this directly in the virtual file, if we like:
::Read Mess:: "From your dead wife.", "neat engraving", "english"

The first part is the deeply moving message. The second is how the writing is described when someone reads it, and the third is the language in which the message is written. Reading this ring thus will give the following:
You read the beefy diamond ring: Written in neat engraving: From your dead wife.

91

Epitaph Online

http://drakkos.co.uk

Tragically moving, isn't it? It's not usually a good idea to include such a read message in the base file, because as far as possible virtual files should be entirely generic for what they are. If we include it in the base file, then anyone who gets a Beefy Ring gets it inscribed with the beautiful poetry of Beefy's wife. Instead, we can configure this message after the ring has been cloned that way the basic ring is defined and usable by anyone, but only Beefy's ring has the engraving. We make the ring available to Beefy in exactly the same way as we made the boots available. We define an object at the top of his setup:
object ring;

Then we clone and move that ring into him:


ring = clone_object (ITEMS + "beefy_ring.jew"); ring->move (this_object());

And then we manually add the read message afterwards:


ring->add_read_mess ("From your dead wife.", "neat engraving", "english");

Almost every item in the game can have a read message attached to it in this way, and it's a nice way to add a little bit of sparkle to otherwise non-specific items.

Item Normalisation
In order to achieve a measure of consistency across Epitaph, most of our items make use of a normalization setting virtual files permit the setting of much of the 'appearance' of an item, but things like weapon damage, weight, condition and monetary value are all 'normalised' they're all calculated from a central source. While that does limit your ability to differentiate the items you create, it comes with many benefits for us it means we don't need to worry about things like new creators developing super weapons all the values are handled internally. The weight of the items you create will be based on, for example, the type of item, its material composition, and so forth.

92

Epitaph Online

http://drakkos.co.uk

Long Dollars
One of the more frustrating aspects of item utilization is when you see something with a filename that looks as if it is the exact item you want. You're looking for a black shirt, and you see /items/clothes/black_shirt.clo, and you go 'Ah yes, my prayers have been answered!' But then you clone that black shirt, and what you find is that it is a ripped, mutiliated black shirt coated in blood and through which your nipples can be seen. All you wanted was a black shirt that anyone could wear, but that clothing is not what you need. We get around this problem on Epitaph by ensuring that items in the /items/ directory are truly generic, and this is done by using what are known as long dollars. If we look at /items/clothes/shirts/simple_shirt.clo, for example:
::Name::"shirt" ::Short::"$colour$ $material$ shirt" ::Long:: "This is a simple $colour$ $material$ shirt. It has absolutely " "no features that distinguish it from any other shirt you may have " "seen in your life.\n" ::Type:: "shirt" ::Material:: (["linen":1]) ::colour:: "white" ::Common Materials:: ({"silk", "cotton", "velvet", "chiffon", "satin", "wool"}) ::Common Colours:: ({"white", "black", "grey", "blue"})

There are several features here that aren't present in the boots you created above for one, there is no direct mention of colour in the short or the long description. The MUD knows what you mean when you put $colour$ in a short or long it means, 'the colour this item has been set to'. Similarly with material it will automatically replace the $material$ code with the material of the item. The result is that I can use set_colour and set_material on this base shirt to create a black silk shirt, a white linen shirt, or a pink leather shirt if I so desire. Common materials and common colours are part of this system when random items are searched up, they will be set to have one of these materials and one of these colours that way people don't always find the white linen shirt they might just find a blue wool shirt instead. In the items you create, please use this long dollars system it gives us the greatest benefit from the items you develop.

More Item Additions


It's not much of a horror setting if we never describe the carnage of the postinfection world. Things should be smeared with blood, or dripping with entrails, or whatever. There has been an intensely violent, bloody daily battle against the 93

Epitaph Online

http://drakkos.co.uk

undead, and that leaves traces behind. However it's usually not particularly effective to describe that in descriptions. I could have for, example a bloody baseball bat, but if every bloody baseball bat has the same gore on it, it looks a bit artificial. Also, what if I want to clean it off? To deal with this, Epitaph has a system of automated gore. To enable it on an item, you use the following in its setup: set_gore (1); You can then see the gore added to the description of the item, like so: This is a steel bladed machete. Machetes are long knives used for hacking away at vegetation and undergrowth. They also make pretty serviceable cleavers for hacking away at limbs. You can see a bloody handprint on the machete. The gore message is generated from a random set of messages and a random set of locations (Such as 'There is a bloody handprint on the $bit$ of $the short$'). This makes it work with the cleaning system that is available through the 'wipe' command . That's straightforward for items, but to see how it works in the add_items we put in our game, we need to handle it as one of the more complex syntaxes for add_item, like so:
add_item (({"stone floor", "stone wall", "stone"}), ({ "long", "There's little to distinguish the stone floor or walls. " "They're just stone... immaculate.", "gory", "stones" }));

When a room gets a gore message added, it picks out all the items that have been marked as potential targets for gore, picks one of the items you provide as a bit, and generates a message something like:
There is a bloodstained handprint on the stones of the stone floor.

If you want it to say something like:


There is a bloodstained handprint on the stone floor.

You simply make the the add_item reference the string for the add_item, like so:
add_item (({"stone floor", "stone wall", "stone"}), ({ "long", "There's little to distinguish the stone floor or walls. " "They're just stone... immaculate.", "gory", "stone floor"

94

Epitaph Online }));

http://drakkos.co.uk

Finally, you can also indicate that an add_item should provide cover and concealment by setting the item as having 'cover' - this makes it available for people to take cover behind in the event of gun-fire. This is done using the normal position commands, but with the 'behind' tag, like so:
> crouch behind counter You crouch behind the fixed counter.

At this point, you gain cover, the amount of which is determined by the add_item, or the cover property of a normal item. Using both cover and gore, you'd create something like this:
add_item ("fixed counter", ({ "long", "The contents of the counter have long since been looted, " "but the counter itself is immovable.", "position", "the fixed counter", "gory", "fixed counter", "cover", 10 }));

The value for the cover part of the add_item is a percentage modifier that is given to the defender when they are attacked by firearms (and also a bonus provided to stealth checks when using a stealth type that takes into account cover). Ten is a roughly appropriate default for this, unless the item is especially solid (like a brick wall).

Conclusion
We've looked at two kinds of item here a pair of boots, and a ring. All virtual objects work on largely the same general principles a file contains a number of settings and the values those settings have. The MUD works out what kind of base object is needed from the extension the virtual object has. It then clones an instance of the base file and configures it with calls (rather than creating a new master object). As far as anyone using the item is concerned, it works identically to any other object written in LPC. Item normalization, and the long dollars system, give us an awful lot of freedom when developing items we don't need to be constrained by worries of game balance or mini-maxing low level values. We have the freedom to concentrate entirely on the things that make our items unique. We don't even need to worry overmuch about colours or materials we can let people set those as they like when they make use of the items. In short, these systems give us the freedom to be creators, not accountants fretting over a spreadsheet of implications.

95

Epitaph Online

http://drakkos.co.uk

An Inside Job
Introduction
We've got the skeleton of our outside rooms in place already in this chapter we're going to look at developing our first inside room a faction shop in which we can make available any further items we develop. As a process, this is a largely identical to creating an outside room, except that we have the extra step of setting up stock and working with the faction code. We'll have cause to encounter some new concepts as we go through this chapter, so there's plenty for us to talk about. We're actually going to put in the base code for two rooms one is a vanilla room that has no special functionality except for a locked door leading to it. The second is the faction shop. On our village map (remember that from chapters two and three?), these will be rooms A and B, and have the following filenames: Map Key A B Filename mysterious_room.c stabby_joe.c

So, with no further ado, Let's Get To It!

The Mysterious Room


Firstly, let's create the template for the mysterious room. Everything we've talked about for our outside rooms works for our inside rooms, but we inherit STD_ROOM instead of STD_OUTSIDE. So, for the mysterious room:
#include "path.h" inherit STD_ROOM; void setup() { set_short ("mysterious room"); add_property ("determinate", "a "); set_day_long ("This is a mysterious room during the daytime. It fairly " "reeks of mystery.\n"); set_night_long ("This is a mysterious room during the nighttime. The " "oppressive darkness hints mysteriously at mystery!\n"); add_day_item ("mystery", "Daytime all around, and yet you cannot see it."); add_night_item ("mystery", "You can't see the mystery for all the " "darkness."); room_day_chat ( ({ 120, 240, ({ "The room emits a sense of mystery.", "No doubt about it, this is a mysterious room.", })})); room_night_chat ( ({ 120, 240, ({ "Was that a mystery there, glinting in the darkness?",

96

Epitaph Online "Maybe the mystery is a grue?", })})); set_light (100); add_exit ("south", ROOMS + "market_northwest", "door"); }

http://drakkos.co.uk

Notice here that the exit we add is of type 'door' that means pretty much what you'd expect it to mean the exit is blocked by a door. We need to match this up with a corresponding exit in market_northwest.c:
add_exit ("north", ROOMS + "mysterious_room", "door");

We've created a door, but it isn't locked. In order to maintain the mystery of the door, we can use modify_exit to lock it shut. Remember how I said earlier that modify_exit had all sorts of cool and interesting powers? Well, let's take a look at what we can do with our mysterious exit here, to heighten it's mystery!

Modifying Exits
The settings to modify_exit come as a listing of pairs of settings and values. With it, we can change the messages people see when they move through the exit, what they see when they look at the exit, and how it behaves when people try to walk through it. We can even add complicated code handlers to an exit you can control extremely precisely under what conditions an exit may be taken. If you want it to be impossible to take an exit while you're carrying iron in your inventory, you can do that. If you want to make it so that entry is permissible only on the 25th of December, you can do that too. We won't talk about that in this section, but we will in a later section of Introductory LPC. The simplest settings just require some numbers and text to describe what should happen. Let's begin simply let's change how the door appears when people look at it. In market_northwest's setup, we add the following:
modify_exit ("north", ({ "door long", "The door hints at mystery within. "likely.\n", })); Riches too, most "

Now, when we look north we'll see:


> look north The door hints at mystery within. It is closed. Riches too, most likely.

If you go north, and look south however, you see:


> look south It's just the south door. It is closed.

The modify exit thus must go in both rooms to which it applies that way you can 97

Epitaph Online

http://drakkos.co.uk

have doors that have one description on one side, and a different one on the other. In mysterious_room thus:
modify_exit ("south", ({ "door long", "The door hints at mystery within. "likely.\n", })); Riches too, most "

If we want to lock it (and we do), we add the locked setting to our modify_exit a 1 indicates the door is locked, a 0 indicates it is not. We also need to provide the name of a key that will open the door: So, in market_northwest:
modify_exit ("north", ({ "door long", "The door hints at mystery within. "likely.\n", "locked", 1, "key", "Mysterious Room Key", }) );

Riches too, most "

mysterious_room will need an identical modify_exit, except that it will modify the south exit rather than the north one. Update our rooms, and voila! The door is locked. Locked doors can be a pain in the backside for a creator, so you can give yourself the ability to walk through such doors by adding the demon property to yourself:
call add_property ("demon", 1) me

The key to this door is something we're going to make available in our shop... a key is any object that has a property matching the name we've given to the key in our modify exit. When the player attempts to take the exit, the MUD looks through all the items on that player for anything that unlocks the door. You can see this in action by picking any random item in your inventory and using the following call:
call add_property ("Mysterious Room Key", 1) <some random object>

Make sure you remove the demon property from yourself though, or you'll just ghost through the door as before:
call remove_property ("demon") me

Modify_exit lets us do much more than we've done here we can set a door to automatically lock after we close it with the autolock setting (1 indicating it autolocks, and a 0 indicating it doesn't). We can set a difficulty for people attempting to lockpick the door using the difficulty setting you give this a value from 1 to 10. The lower the value, the easier the lock is to lockpick the values, as you might expect, correspond to the difficulty levels set in task_mapper.h. We can also change the messages people see when objects pass through the door by setting three values in the same manner as we did for Beefy's move message. 98

Epitaph Online

http://drakkos.co.uk

For example, we could do the following in market_northwest:


"enter mess", "$N mysteriously enter$s the room. How mysterious!", "exit mess", "$N exit$s through the mysterious door. What wonders " "must be found within?", "move mess", "You walk through the door, excited by the possibilities " "within!\n",

And in the mysterious_room:


"enter mess", "$N mysteriously enter$s from the mysterious door. What " "wonders were seen on the other side of that portal?", "exit mess", "$N exit$s through the mysterious door.", "move mess", "You leave the room, satisfied its mysteries have been " "revealed to you.\n",

Enter mess is what people see when someone arrives in a room through the exit. Exit mess is what people see when someone leaves through the exit. Move mess is what gets displayed to the player. So, we've given the door to our mysterious room enough mystery for now. Let's make the key available to those who may be tempted to explore.

Shop 'Till You Drop


We're going to create a faction shop, which is a shop that is associated with a faction (that faction has already been created for you). Faction shops buy and sell using faction reputation rather than anything as gauche as 'money'. The inherit we use for this is STD_GENERAL_SHOP. Let's create the basic template for that room, and hook it up to our marketplace:
#include "path.h" inherit STD_GENERAL_SHOP; void setup() { set_short ("Stabby Joe's Emporium of Wonder"); add_property ("determinate", ""); set_long ("This is Stabby Joe's Emporium of Wonder, where he sells " "wonderful things. He also stabs people.\n"); set_light (100); add_exit ("south", ROOMS + "market_northeast", "door"); }

And in market_northeast, we need an exit linking back to the shop we're setting up:
add_exit ("north", ROOMS + "stabby_joe", "door");

Update both rooms, and wander into the shop. Sadly, if you try to list or browse 99

Epitaph Online

http://drakkos.co.uk

you'll find that you can't it will say:


Please notify a creator: the storeroom for this shop cannot load or has gone missing.

It's okay, because you're a creator! The storeroom for a general store is actually another room you create, except that's not how it works for faction shops. When you create a faction shop, it draws the inventory of the shop from the faction data file. We just need to link up this room to the faction system, which we do by adding this line into the shop's setup:
set_faction ("deadville");

This marks the shop as belonging to the Deadville faction, which is a creator only example faction. You'll find now that you can browse, list, buy and sell items, and that it even has some inventory as discussed above. Inventory for this faction is shared amongst all faction shops, so you may find strange stuff appearing in it as a result of the actions of your fellow creators!

Stabby Joe
The MUD won't force you to have a shopkeeper for the shop, but it's a good idea to have one it adds a sense of immersion that is otherwise lacking. So let's create our second NPC Stabby Joe! He's going to be our shopkeeper and resident psycho. We're going to give him an add_response for his cousin, but we're not going to do anything with it right away. Save him in your chars directory under the name joe.c:
#include <npc.h> #include "path.h" inherit STD_MONSTER; void setup() { set_name ("joe"); set_short ("Stabby Joe"); add_property ("determinate", ""); add_property ("unique", 1); add_adjective ("stabby"); add_alias ("stabby"); set_gender (1); set_long ("This is Stabby Joe, Deadville's main shopkeeper and " "resident psycho. While generally at peace, he can be riled " "into fits of towering rage by making reference to his cousin, " "Slicey Pete.\n"); basic_setup ("human", "survivor", MONSTER_LEVEL_NORMAL); setup_nationality (MUDLIB_NATIONALITIES + "british", "scotland"); load_chat(10, ({ 1, "' Buy my stuff, or I'll kill you.", 1, "' Good prices on all my stuff! If you can find anything cheaper " "in all of Deadville, I'll kill you!", 1, "' Satisfaction guaranteed, or I'll kill you!",

100

Epitaph Online })); load_a_chat(20, ({ 1, "' cut cut cut cut cut cut cut cut!", 1, "' Gods, that was violent! I blame... the sea!", })); add_response (({"slicey", "pete"}), "#cousin_response"); dress_me(); }

http://drakkos.co.uk

Everything here is As We Know It. We load him in a room slightly differently though we just call set_shopkeeper in the shop and give the path to the NPC. The general shop code will handle the rest, including bringing him into the room on resets. So, in our stabby_joe shop:
set_shopkeeper (CHARS + "joe");

Update the room, and there's our shopkeeper. You will find, right away, that if you dest Joe you'll no longer be able to make use of his shop the facilities are available only while he is there to serve you.

Conclusion
In this section of the notes we've explored a little more of the power available in modify_exit, and set up a faction shop complete with stock. In the next section, we're going to look at filling out the functionality that's missing here we're going to need to discuss a new topic in the next section, one that's hopefully going to tie up a lot of what we've been doing into a cohesive whole.

101

Epitaph Online

http://drakkos.co.uk

Dysfunctional Behaviour
Introduction
In this chapter we're going to talk about something that will hopefully clarify how everything on Epitaph actually fits together. Of a necessity, we have to be quite selective in the theory we cover in this text we only introduce theory as it impacts on what it is we're trying to do in the examples. As such, there's a lot of 'put this thing here in this way, but don't worry about why'. Hopefully after you've worked your way through this chapter, a whole lot of what you're doing will make more sense.

Our Task
We've left our shop, and Stabby Joe himself, in a state of incompleteness. While he runs his shop perfectly well (for a given value of perfectly well), he's got a response dangling there that just isn't being used at all. We want Stabby to fly into a fit of apoplectic rage when his cousin, Slicey Pete is mentioned. However, this requires us to do more than set a few values in a file with pre-written functions it needs us to write some code of our own. There's one place already you've written actual code, and that's in the vaulting of the rock in street_03. Sometimes we want to do something very specific in our areas, and in order to do that we need to go beyond the generic functionality provided by our inherits for that we need to make use of a programming system called functions. They are also sometimes called methods, and in this chapter we will use the two terms interchangeably.

The Science Bit... Concentrate!


A function is a little section of code that you tag by a name. You've already been writing functions setup and reset are functions, as is after_reset and do_vault. These are functions that you write so that your rooms do what you want them to. At certain points, the MUD will execute the code that goes with your function in technical jargon, the MUD calls your function. When you room is created, setup is called. When the MUD wants to reset the internal state of your room, reset is called. You have no control over these two events if you change the name of your setup function to set_me_up, you'll find the room doesn't work. This is a convention to which you must adhere. However, do_vault is a function that you told the MUD to call. As such, it doesn't 102

Epitaph Online

http://drakkos.co.uk

matter what name you give them as long as the name is meaningful. You set up the connection between your rock and your function using that weird 'happy face' notation, like so:
add_item ("jagged rock", ({ "long", "This is a jagged rock.", "position", "on the jagged rock", "vault", (: do_vault :), ({"kick", "punch"}), "Ow! That stung!\n", }));

If you change it thusly:


add_item ("jagged rock", ({ "long", "This is a jagged rock.", "position", "on the jagged rock", "vault", (: jump_over_rock :), ({"kick", "punch"}), "Ow! That stung!\n", }));

It would instead call the jump_over_rock method when you attempt to vault it. It's our choice what we call this function however, it is an informal convention on the MUD that when we have a function that is called in response to a player performing a command, we call it do_<command> (such as, yes, in do_vault!). As long as the name of the function you define is the same as the function you tell the MUD to call, everything will be hunky dory. However, the conventions we adopt for naming these functions were adopted for a reason - they make it considerably easier for people to read and maintain code if they know where they are to look for certain pieces of functionality.

The Structure of a Function


A function has a very particular structure to which it must adhere when written.
<return_type> <name_of_function> (<parameter_list>) { }

First, it must declare its return type that's the kind of information that comes out of the function when it's finished. Our do_vault function returns a string that contains the message a player should see, so the function returns a string. Next comes the name of the function we've already seen this in action and should be familiar with it by now. The parameter list is something we haven't encountered yet (formally we've actually been using parameters all along) it's information that is provided to our function by the code that calls it. Sometimes the MUD provides information for you when you tell it a function is to be called at some point. Other times you have to provide it yourself.

103

Epitaph Online

http://drakkos.co.uk

A parameter list consists of pair of variable types and names, separated by a comma. For example, consider a very simple function that takes two whole numbers and adds them together;
int add_numbers (int num1, int num2) { }

The braces indicate the ownership of a function all code that exists between these two braces consist of the code belonging to it. These are the body of the method. Within the body of the method, we cam make use of variables defined in the parameter list just as if they had already been defined and assigned values:
int add_numbers (int num1, int num2) { return num1 + num2; }

When the MUD gets to a return statement in the function, it stops executing the code that belongs to to the function and returns whatever value was indicated to the code responsible for calling the method. Some functions don't return any value, and we indicate those as being of return type void (like setup and reset). They require no return statement in the body of their code, and if you wish to terminate them before executing all of the code, you can use a return statement by itself:
return;

We add functions to our code whenever we want to make a certain piece of code repeatable rather than copying and pasting the code, we create a function. This means that if the code is broken in one part of the program, we don't need to fix it in all the other places we pasted it we fix it in the function, and it's fixed everywhere. Finally, when we want to make use of a function, we simply tell the MUD to call it by giving its name, and any parameters we wish to send it:
add_number (4, 5);

Does that look vaguely familiar? It should it's how you've been setting up all of your objects so far with a series of function calls in which you provide parameters to functions that have already been written. This function call won't do anything we can see, because although it will add the two numbers together, the sum of these numbers will never get stored anywhere. In the same way that you need an object variable to hold a pair of boots or a ring on the MUD, you need an integer variable to hold the returned value of your function:
int num; num = add_number (4, 5);

At the the end of this, the variable num will have the value 9. 104

Epitaph Online

http://drakkos.co.uk

This should hopefully be making it clearer what's happening in a lot of your code while we haven't done a lot with dealing with returned variables, pretty much everything we've been doing has been through function calls. When we added the code to give Beefy his constant heart attacks, we used the find_object method, which returned an object reference. Everything you have done so far has been built on the use of functions. Functions are the engine that drive the MUD.

A Little Bit More...


Okay, let's talk about just a little bit more theory and then we'll actually go on to do something with these functions. Functions on Epitaph are actually broken up into three types. The first of these are local functions, or lfuns. They are functions that are defined inside a game object. For example, add_property is a function that is defined in /mudlib/mixins/basic/property.c, and any object that inherits that file will have access to add_property. Objects that don't inherit that file will no have access to that method. Luckily the objects we're creating all have that handled for you, so properties are available to all your rooms, NPCs and items. Local functions can be called directly using the call command we've done this a few times as a creator. Call lets us manually call a function, providing the parameters as we do so. The returned value of a call is displayed to us, but we can't do anything with it. They can also be called in other objects in code using the -> operator, such as when we do something like:
beefy->do_command ("dance madly");

The -> operator tells the MUD 'call the local function do_command on the object referenced with the name beefy'. The second type of function are external functions, or efuns. These are hardcoded into the driver and are available to all objects regardless of what they inherit. You cannot call these functions with the call command, but you can make use of them with the exec command. They do not get used on objects with the -> operator, because that operator requires the function to be 'defined locally'. These cannot be changed easily, as they require a new version of the driver to be developed and installed. The environment() function is an example of such an efun. We call it like this:
object env = environment (beefy);

We do not call it like this:


object env = beefy->environment();

105

Epitaph Online

http://drakkos.co.uk

The last type of function are simulated efuns, or sfuns. While they are not hardcoded into the driver they are defined in a MUD object (/secure/simul_efun) that makes them available to all of our objects just like an efun. There is no difference to you was a creator between an efun and an sfun the difference is in terms of performance. Since efuns are defined in the driver, they are executed much more quickly than an equivalent sfun would be. Sfuns can also be changed and added much easier than efuns can, although the set of people who can actually add or change sfuns is fairly limited. You'll often find new sfuns being made available more often than new efuns are though.

Function Scope
Like variables, functions have a scope you can't directly make use of a function that is defined in an object other than your current object. You need to use the special arrow notation we've seen for example, when we did our task check:
TASK_MAPPER->perform_task (this_player(), TASKMAP_VAULT_BARRIER, TTSM_DIFF_INCREDIBLY_EASY, 0, TM_FREE);

TASK_MAPPER is an object that is defined in task_mapper.h, and what this code is telling the MUD is 'call the method perform_task on the object defined as TASK_MAPPER'. There is a secondary syntax for this using an efun called call_other. Call other works like this:
call_other (beefy, "do_command", "say Cor, this is cool.");

Functionally, this is identical to the following syntax:


beefy->do_command ("say Cor, this is cool.");

The main benefit is that since the function to be called through call_other is defined as a string, we can actually have variable method calls:
string str = "do_command"; call_other (beefy, str, "say Cor, this is even cooler than before!");

I don't recommend you actually do this at this point, but you may see it in use throughout the code you read and you should know what's happening when you see it.

Onwards and Upwards!


Okay, now we've gotten that out of the way let's incorporate a few functions into our new objects. First of all, let's define what Stabby Joe is going to do when you mention his cousin. Firstly, we're going to keep an internal 'anger counter' for 106

Epitaph Online

http://drakkos.co.uk

Stabby. This is a number against which we'll roll a random number. If the number we roll is less than his anger, he's angry! If it's more, then he's not quite so angry. Players mentioning his cousin will receive one of two responses violence or a warning. The exact response a player will receive will depend on the anger check. First of all, let's write a stub function a stub is a function that has no real code in it, but is there so that our object will actually compile when we refer to it:
int anger_check() { return 1; }

We're going to tell add_respond_to_with that it should use a function to determine how it responds to people mentioning his cousin we do this by giving the name of the function as a response, with a # symbol at the start of it, like we did in the last chapter:
add_response (({"slicey", "pete"}), "#cousin_response");

So, whenever this response is triggered, it will call the method cousin_response. Be aware that you can't use this syntax for everything for example, the following will not work:
set_long ("#random_long");

It only works in specific cases, and you should consult the documentation on the function you are using to see whether or not this syntax is supported. It does work as a chat in load_chat and load_a_chat though, which is very useful. Anyway, back to cousin_response we add in a stub for this to make him say that he doesn't like his cousin.
void cousin_response() { do_command ("' Don't mention my cousin, or Stabby cut your pretty face!"); }

We put these in as stubs first to ensure that everything is working properly we need to ensure that the connection between our functions is set up before we start writing any more complicated code. First we check the link between our add_respond_to_with and our cousin_response function. We can easily see this simply by mentioning the name to Joe to see if he responds:
> ask joe about pete You ask Stabby Joe a question about pete. Stabby Joe exclaims: Don't mention my cousin, or Stabby cut your pretty face!

Right, that's working so now we link up cousin_response and anger_check to see if they connect up properly:
void cousin_response() { if (anger_check() == 1) { do_command ("' I KILL YOU!"); do_command ("kill " + file_name (this_player())); }

107

Epitaph Online

http://drakkos.co.uk

else { do_command ("' Don't mention my cousin, or Stabby cut your pretty face!"); } } int anger_check() { return 1; }

Note the way in which we're handling the killin', using the file_name efun:
do_command ("kill " + file_name (this_player()));

The file_name of an object is an unambiguous unique reference to the object. Find our your own with the following command:
exec return file_name (this_player());

Then, using the reference it gives you, try interacting with yourself. For example, if your reference was /global/lord#7325 (which mine was), then try:
/smile global/lord#7325 You smile at yourself.

This means we don't need to worry about targeting specific objects by their names we target them by their references. This is important, it solves several weird issues with targeting and means that your NPC can genuinely differentiate between two objects with the same name. Anyway, update Joe, and... wait, what's this? It comes up with a syntax error that says:
Undefined function anger_check before == 1) {

But what does it mean, undefined function? We've defined it right there, in the code! Actually, this is a layover from the C programming language upon which LPC is based the MUD reads in our file top to bottom, and it gets to the anger_check function call before it gets to the definition of the function itself. It panics, and halts the process saying 'Hang on! I don't know anything about this anger_check to which you refer! ABORT, ABORT!' We can simply swap the two functions around to solve the problem:
int anger_check() { return 1; } void cousin_response() { if (anger_check() == 1) { do_command ("' I KILL YOU!"); do_command ("kill " + file_name (this_player())); }

108

Epitaph Online

http://drakkos.co.uk

else { do_command ("' Don't mention my cousin, or Stabby cut your pretty face!"); } }

That's an inelegant solution though you shouldn't have to dramatically alter the layout of your code just to make it run. There's a better solution that involves just telling the MUD 'Don't worry, this function is defined later in the program'. It's called function prototyping, and we already did it earlier when we wrote the do_vault function. At the top of your code, just after the inherit (but not before, or you'll introduce a new error) you simply put the definition of the function its return type, its name, and the parameters, like so:
int anger_check();

This is the function definition, and as long as LPC sees that before it gets to the function call, it will be happy. There's no need to restructure your code around such an error, just add a function prototype. Now when we mention Joe's cousin, he says he'll kill us, and actually makes an attempt to do so:
> ask joe about pete You ask Stabby Joe a question about pete. Stabby Joe exclaims: I KILL YOU! Stabby Joe moves aggressively towards you!

Progress is being made!

Violence Begets Violence


So, every time someone mentions Pete's cousin, we want to increase our anger counter. For that, we first need an anger counter!
void cousin_response() { int anger_counter; anger_counter = anger_counter + 1; if (anger_check() == 1) { do_command ("' I KILL YOU!"); do_command ("kill " + file_name (this_player())); } else { do_command ("' Don't mention my cousin, or Stabby cut your pretty face!"); } }

And then in anger_check, we roll a random number against that anger_counter to see if he's angry:
int anger_check() {

109

Epitaph Online int anger_check() { if (random (100) < anger_counter) { return 1; } return 0; }

http://drakkos.co.uk

However, when we update Joe the MUD will once again complain. It's our old friend, the scope problem. Because anger_counter is defined locally in cousin_response, anger_check doesn't have access to it. We could solve this by passing it as a parameter. First we'd need to change our function prototype:
int anger_check (int);

Then hook up the functions in the new way:


void cousin_response() { int anger_counter; anger_counter = anger_counter + 1; if (anger_check(anger_counter) == 1) { do_command ("' I KILL YOU!"); do_command ("kill " + file_name (this_player())); } else { do_command ("' Don't mention my cousin!"); } } int anger_check(int anger_counter) { if (random (100) < anger_counter) { return 1; } return 0; }

That solves our scope problem, but it doesn't solve our persistence problem just like with vaulting the rock, if we create the variable locally it gets recreated every time the function is called anger counter will never increase beyond one. We solve the problem then by having the code linked up in the original way, but make anger_counter a class wide variable instead:
#include <npc.h> #include "path.h" inherit STD_MONSTER; int anger_counter; int anger_check(); void setup() { // Usual setup stuff } void cousin_response() { int anger_counter; anger_counter = anger_counter + 1;

110

Epitaph Online

http://drakkos.co.uk

if (anger_check() == 1) { do_command ("' I KILL YOU!"); do_command ("kill " + file_name (this_player())); } else { do_command ("' Don't mention my cousin, or Stabby cut your pretty face!"); } } int anger_check() { if (random (100) < anger_counter) { return 1; } return 0; }

We won't add a reset for anger_counter Joe just gets angrier, he never calms down over time. It can take a while before we can tell whether Joe is angry enough to strike, so let's add another function that we can call to tell us his internal state:
int query_anger() { return anger_counter; }

Just to make sure that it's all working the way we expect:
> call query_anger() joe Returned: 0 > ask joe about pete You ask Stabby Joe a question about pete. Stabby Joe exclaims: Don't mention my cousin, or Stabby cut your pretty face! > call query_anger() joe Returned: 1 > ask joe about pete You ask Stabby Joe a question about pete. Stabby Joe exclaims: Don't mention my cousin, or Stabby cut your pretty face! > call query_anger() joe Returned: 2

His anger is increasing nicely! Let's make it easier to test him by allowing ourselves a way to set his anger high enough so that we don't need to repeat the same thing over and over again:
void set_anger (int val) { anger_counter = val; }

Now set it to something that will definitely trigger his rage, such as one hundred, and try say his name: 111

Epitaph Online > call set_anger (100) joe Returned: 0 > call query_anger() joe Returned: 100 > ask joe about pete You ask Stabby Joe a question about pete. Stabby Joe exclaims: I KILL YOU! Stabby Joe moves aggressively towards you!

http://drakkos.co.uk

That's one psycho shopkeeper, right there!

Conclusion
This chapter is somewhat 'think heavy' because of the need for us to talk about how functions work they are critical to gaining any real understanding of how bits of the MUD communicates with other bits, and so it's worth our while talking about them properly. Hopefully by this point you've now gained a fairly strong understanding of why the magic words you're typing actually make the MUD do interesting things! We're not done yet of course, there's plenty more for us to talk about before we reach the end of Introductory LPC!

112

Epitaph Online

http://drakkos.co.uk

Going Loopy
Introduction
For the moment, Stabby's shop is not exactly exciting in terms of its stock it gets only what is set in the faction handler. In this chapter, we're going to look at how we can provide some more bespoke stock options, and and in the process we're going to be introduced to the next of our major programming structures the loop. So, let's not stand around chattering all day let's roll up our sleeves and have at it like the LPC crusaders we are!

Stabby's Shop
We're always going to get the normal faction stuff as part of having a faction shop, but we're not limited to that we can also add in our own stock as we go along. That way we can have shops that all serve a single faction, but each can be distinguished by having their own unique supplies where it is appropriate which it is, now! In our shop, we have a set of functions that let us relatively easily manipulate the store room. One of these is query_num_of, which allows us to pass in an identifier and get how many items match that identifier. The other is add_to_storeroom, which takes an object and adds it to the current stock. We're going to use these functions to add the boots and ring we created earlier to the shop. Yes, I know they are Beefy's equipment, but to hell with him. What has he done for us recently? Nothing, that's what. To add one of each is pretty straightforward in the setup of our shop, we have the following:
add_to_storeroom (ITEMS + "beefy_ring.jew"); add_to_storeroom (ITEMS + "beefy_boots.clo");

This will make the stock available, but only once it will never replenish itself. That may be what we want, but it's probably not. Instead, what we want to do is provide a reset function that ensures the restocking:
void reset() { ::reset(); add_to_storeroom (ITEMS + "beefy_ring.jew"); add_to_storeroom (ITEMS + "beefy_boots.clo"); }

Unfortunately, there is a flaw here every reset, without any kind of check, a new ring and a new pair of boots are added to the storeroom. Leave this running for a 113

Epitaph Online

http://drakkos.co.uk

couple of days, and there will be dozens of both. We don't want that, we want to limit it to something sensible. Let's say ten:
void reset() { ::reset(); if (query_num_of ("beefy ring") < 10) { add_to_storeroom (ITEMS + "beefy_ring.jew"); } if (query_num_of ("beefy boots") < 10) { add_to_storeroom (ITEMS + "beefy_boots.clo"); } }

Every reset, we'll get another one of each of these until we have a total of ten. That's better, but it's still not ideal what we really want is for it to start off with ten of these, and then even reset restock it back to the full amount. Well maybe that's not really what we want, but it's what we're going to do.

Long Hand
So, using the tools we already have, how would we accomplish this task? Well, we could do something like:
void reset() { ::reset(); if (query_num_of ("beefy ring") < 10) { add_to_storeroom (ITEMS + "beefy_ring.jew"); } if (query_num_of ("beefy ring") < 10) { add_to_storeroom (ITEMS + "beefy_ring.jew"); } if (query_num_of ("beefy ring") < 10) { add_to_storeroom (ITEMS + "beefy_ring.jew"); } if (query_num_of ("beefy ring") < 10) { add_to_storeroom (ITEMS + "beefy_ring.jew"); } if (query_num_of ("beefy ring") < 10) { add_to_storeroom (ITEMS + "beefy_ring.jew"); } if (query_num_of ("beefy ring") < 10) { add_to_storeroom (ITEMS + "beefy_ring.jew"); } if (query_num_of ("beefy ring") < 10) { add_to_storeroom (ITEMS + "beefy_ring.jew"); } if (query_num_of ("beefy ring") < 10) { add_to_storeroom (ITEMS + "beefy_ring.jew");

114

Epitaph Online } if (query_num_of ("beefy ring") < 10) { add_to_storeroom (ITEMS + "beefy_ring.jew"); } if (query_num_of ("beefy ring") < 10) { add_to_storeroom (ITEMS + "beefy_ring.jew"); } }

http://drakkos.co.uk

Blurgh. Hopefully by now you should be aware of why this is a bad solution it has a lot of copying and pasting, and if we ever need to change the code for cloning an object, we need to change it ten times. That's not so bad in itself, but once we do that for the boots we have twenty lines of code we'd need to change. If we add in more objects, there is more we need to fix. Programmers, you see, are fundamentally lazy we don't believe that we should do any work if the computer can do it for us. Luckily, there is a programming structure that will do this for us the loop.

Loops
When we want to repeat a piece of code a multiple number of times, we provide the MUD with a loop to make it do the actual work for us. The simplest of these loops is called the while loop, and it will repeat code until a particular continuation condition is no longer met. For example, imagine the following:
void reset() { ::reset(); while (query_num_of ("beefy ring") < 10) { add_to_storeroom (ITEMS + "beefy_ring.jew"); } }

The continuation condition works just like the condition in an if statement. The difference is that the while loop will keep doing the code in the braces until the condition is no longer met. In this case, it will continue to perform the code within the braces while query_num_of returns a number that is less than ten. That's it that is all you need to do! We can then add another while loop to handle the boots:
void reset() { ::reset(); while (query_num_of ("beefy ring") < 10) { add_to_storeroom (ITEMS + "beefy_ring.jew"); } while (query_num_of ("beefy boots") < 10) { add_to_storeroom (ITEMS + "beefy_boots.clo"); } }

115

Epitaph Online

http://drakkos.co.uk

Notice that we do this with two separate loops rather than one loop handling both that's because there may be different numbers of rings and boots in the shop (perhaps the rings are selling especially well, or maybe the boots have just been featured on the feet of an influential celebrity), and we want to make sure that both of the items are appropriately restocked.

Doing While...
A do while loop works in exactly the same way as a while loop, except that its guaranteed that the code will be executed at least once. If there were already ten of an item in the storeroom, our while code would never execute. With a do_while, we can ensure that the code is run once before the continuation condition is checked:
do { add_to_storeroom (ITEMS + "beefy_ring.jew"); } while (query_num_of ("beefy ring") < 10);

Do while loops have somewhat specialised use - if you can't think of a reason why you would want to do this, it's fine... you don't have to use them unless you actually need to, but when you do need to they are invaluable. While loops and do while loops are examples of a set of structures called unbounded loops - their continuation can be dependant on unknown constraints. Here we're working with numbers, but that's not where they are best used. They are best used in situations where we don't know how many time we're going to loop. For example, consider the following:
while (there_are_enemies_in_the_roo) { kill_them(); kill_them_all(); }

We don't know when the enemies will leave the room - they may be killed, they may choose to run away - it's something over which we have no control and thus we manage that uncertainty by using a while loop.

116

Epitaph Online

http://drakkos.co.uk

For Loops
We can use a for loop to deal with situations in which we know how many times a piece of code will be executed. That doesn't mean that we know when writing the code how many times we have to execute the loop, just that when the MUD gets to the loop, somewhere we have a variable storing how many times it is going to need to loop over the code. This is a bounded loop. It has all the parts of our first while loop, just presented in a slightly different syntax. If we wanted to make ten rings available, then:
int counter; for (counter = 0; counter < 10; counter = counter + 1) { add_to_storeroom (ITEMS + "beefy_ring.jew"); }

The for loop handles keeping the counter variable correct for us - we can concentrate purely on the functionality we wish to repeat. It otherwise works like a standard while loop:
for (initialization ; continuation ; upkeep) { // code goes here }

A for loop can also declare a variable as part of its counter inside the loop declaration, neatly encapsulating everything into a single syntactic structure, like so:
for (int counter = 0; counter < 10; counter = counter + 1) { add_to_storeroom (ITEMS + "beefy_ring.jew"); }

You can also write the upkeep in a more compact fashion:


counter++;

Or :
counter += 1;

Both of these lines of code do (almost) the same thing - they increase the value of num_carrots by one. They actually do very subtly different things in the way they increase the value, but that's of no consequence to us at the moment:
for (int counter = 0; counter < 10; counter++) { add_to_storeroom (ITEMS + "beefy_ring.jew"); }

As the code is currently written, we've made it so that every reset, ten rings get cloned into the store-room. That's really not what we want (for the same reason that we don't want to clone a single ring every reset, just multiplied by ten).

117

Epitaph Online

http://drakkos.co.uk

Luckily, the for loop gives us fine-grained control over our counter if we want to start it counting from a number other than zero (say, for example, the current number of items in the storeroom), we can do that:
void reset() { int current_count; ::reset(); current_count = query_num_of ("beefy boots"); for (int counter = current_count; counter < 10; counter++) { add_to_storeroom (ITEMS + "beefy_ring.jew"); } }

The reason that this kind of approach works is because of the way the for loop works the first thing it does is handle the initialization portion of the loop (setting the counter to current_count). Then, it checks the continuation portion (is our counter less than ten). If and only if that continuation portion evaluates to true will it perform the loop. Once the loop has been performed, it then executes the upkeep code (the third 'compartment' in the for loop). Initialization happens once, before anything else is done. The continuation happens at the start of each loop, and determines whether the code between the braces should be executed. The upkeep happens only at the end of the loop. Each of these loops around a structure is called an iteration so the for loop checks the condition, iterates if it should, and then executes the upkeep.

Conclusion
This chapter introduced us to the loop a tremendously powerful programming structure that lets us make the MUD do a lot of work for us. Whenever we need to repeat code, we put in a loop, and wave an imperious hand as we do. It means we don't need to copy and paste, and that we have an easy way of adjusting the number of times code must repeat, sometimes on the basis of information we simply don't have when we're writing the code. That's extremely useful, and you'll find it a powerful ally in your quest to master the Epitaph mudlib.

118

Epitaph Online

http://drakkos.co.uk

Arrays, You Say?


Introduction
In this chapter we are going to discuss one of the most important coding tools that you have available to you as a creator the array. Arrays are one of the most important concepts to understand if you want to provide genuinely interesting code in your areas, and it's worth reading over this chapter obsessively until it all makes sense. Then, practice, practice, practice until you are entirely comfortable with the topic. I honestly can't stress this enough an understanding of arrays forms the most important skill of a creator, as all of the more complex subjects are inextricably linked with it. We're going to explore arrays in this chapter through the medium of Slicey Pete, Joe's cousin. Slicey Pete is a wandering vagrant who scavenges the streets of Deadville for its meager salvage opportunities. We'll also continue with the topic in the next chapter. So, with no more ado, on with the show!

Slicey Pete
Pete isn't just a normal NPC he's a wandering merchant, and thus he uses a different inherit. Beyond that, he works like all of the other NPCs we've developed this far. Let's look at his template:
#include <npc.h> #include "path.h" inherit STD_MONSTER; void setup() { set_name ("pete"); set_short ("Slicey Pete"); add_property ("determinate", ""); add_property ("unique", 1); add_adjective ("slicey"); add_alias ("slicey"); set_gender (1); set_long ("This is Slicey Pete, the cousin of Stabby Joe. While people " "often assume the worst because of his name, he's not a violent man. " "He just used to be a butcher.\n"); basic_setup ("human", "survivor", MONSTER_LEVEL_NORMAL); setup_nationality (MUDLIB_NATIONALITIES + "british", "scotland"); add_move_zone ("deadville"); set_move_after (30, 60); load_chat(10, ({

119

Epitaph Online 1, "' Stabby's real name is Madeline, you know.", 1, "' Poor Madeline - he was such a nice boy at one point.", 1, "' So hungry.", })); load_a_chat(20, ({ 1, "' No, I was just a butcher! Leave me alone!", 1, "' I think I just had an accident!", })); add_response (({"stabby", "joe"}), "' He was a nice lad once."); dress_me(); init_equip(); }

http://drakkos.co.uk

So far, so good there's nothing really new here. His interest lies in what he's going to do as he wanders around the game specifically, he's going to pick up everything he finds, and eat it. It doesn't matter what it is guns, concrete, smaller animals he'll just pick them up and toss them in his mouth hole. There are various ways in which we can handle this, but the simplest way is to simply give him a load_chat that calls a function every so often that function will be 'eat_everything':
load_chat(10, ({ 1, "' Stabby's real name is Madeline, you know.", 1, "' Poor Madeline - he was such a nice boy at one point.", 1, "' So hungry.", 1, "#eat_everything", }));

In this function, we'll do all the magic work of having him consume all around him (except for players, and perhaps other NPCs). For now, just put in a small skeleton for the function:
void eat_everything() { do_command (": looks around for something to eat."); }

Finally, let's spawn him in a room for this, since he's not a shopkeeper, we use the usual add_unique_spawn routines we used for Captain Beefy. Let's have him spawn in street_02, just for the hell of it:
add_unique_spawn (CHARS + "pete", "$N appear$s with a pop.", "$N disappear$s with a pop.");

The stage is set, the lights are dimmed, but before we get to the eatin', let's talk a bit about arrays.

120

Epitaph Online

http://drakkos.co.uk

The Array
Simply stated, arrays are just lists of data. You've been using them all along, although we haven't stressed then. You've encountered an array whenever you've seen a line of code like:
({"some", "things", "here"})

Arrays are phenomenally useful, and it's fair to say that until you have mastered them you are only paddling around in the shallow end of programming. Let's talk about how they work! Arrays are just variables, the difference is that they have multiple compartments into which data can be inserted. All the data has to be of the same type. We declare them slightly differently from the variables we have seen so far they are indicated with a star symbol by the variable's name. To create an array of strings, we'd use the following syntax:
string *my_array;

We also set it with a value in a slightly different way to create an empty array, we use the ({ }) notation:
my_array = ({ });

We need to set the array to have something before we use it, or we'll likely get Terrible Errors. If we want to set an array up with some starting values, then we use a variation on that syntax:
my_array = ({"some", "things", "here"});

When this code is interpreted by the MUD, it sets up a variable with three compartments: Compartment 0 1 2 Contents "some" "things" "here"

The number of the compartment is called its index, and the contents are known as the element, as in 'Give me the element at index 2'. We can pull information out of these compartments using a special notation:
string bing = my_array[1];

This code will pull out the element at index 1 (in the case of our example, it will be the string "things"). If we want to change a value, then we can do that too: 121

Epitaph Online

http://drakkos.co.uk

my_array[1] = "pirates!";

After this line of code is executed, our array will look like this: Index 0 1 2 Element "some" "pirates!" "here"

In both cases you must be careful not to access an index that is either larger than the size of the array, or a negative number. If you do, the MUD will complain of an 'array index out of bounds', which just means you tried to access an element that doesn't actually exist. If we want to add in a fourth element, then we do that by adding another array to our first:
my_array += ({ "matey" });

This adds the new element to the end of the array: Index 0 1 2 3 And to remove an element, like so:
my_array -= ({ "here" });

Element "some" "pirates!" "here" "matey"

This will delete every instance of the word 'here' in the array: Index 0 1 2 Element "some" "pirates!" "matey"

If you want to delete an element at a specific index, you use the delete sfun. This takes three parameters the name of the array, the index from which you want to delete, and how many elements you want to delete:
my_array = delete (my_array, 1, 1);

The result of this would be: Index 0 1 Element "some" "matey" 122

Epitaph Online

http://drakkos.co.uk

It may not seem immediately obvious as to why this is so useful, but we'll see before too long. Oh yes, you'll see. YOU'LL ALL SEE.

Array Indexing
LPC provides a number of ways in which we can index elements without simply using integers. We can start indexing from the last element by using the < operator within an array index notation. If we want the last element in an array, we can do it like so:
string str = my_array[<1];

Or if we want the second last:


string str = my_array[<2];

We can get a range out of an array by using the .. notation, like so:
string *sub_array; sub_array = my_array[1..2];

The range notation is inclusive, so with the above code you would get elements 1 and 2 out of it. You can also combine these notations:
string *sub_array; sub_array = my_array[0..<2];

The effect of this array index would be to get all of the elements from the first element (indexed by 0) up until the second last element (indexed with <2). You can even leave off the end part of the range:
string *sub_array; sub_array = my_array[1..];

This would get all of the elements of the array from the second one onwards. There's a lot that we can do with this kind of indexing system, but don't worry about it just yet. Just know that when you see it, that's what's happening.

Array management
Arrays have conceptual complexity to go with them, and as such there are a number of 'management' functions that exist to help you manipulate them. Perhaps the most important of these is sizeof, which gives you the number of elements in an array:
int num = sizeof (my_array);

With our two elements, the variable num will contain the value 2. We can then use 123

Epitaph Online

http://drakkos.co.uk

this as a basis for array manipulation after all, to manipulate an array properly we need to know how big it is - that makes it amenable to manipulation with a bounded loop. Perhaps the next most important task we perform on a day to day basis with an array is to tell if a particular value is contained with. We do that using the member_array function. This take two parameters one is the value we wish to search for, and the second is the array through which we wish to search. The function returns a -1 if the item is not present, or the index of the first match if it is:
int found = member_array ("matey", my_array);

With this example, found will be equal to 1 because that's where the search text can be found. If we search instead for 'me hearties', found will be set to -1 because that string is not present in the array. If we want to pull a random element out of the array, we can use the element_of function:
string random_element = element_of (my_array);

And we can get several random elements out of the array using the elements_of function. The first parameter to this method is the number of elements you want out of it. The second is the array from which you want to extract them.
string *some_elements = elements_of (2, my_array);

We can randomise the order of the contents of an array, just like a pack of cards, with the shuffle method:
my_array = shuffle (my_array);

You can also sort the contents of an array into ascending or descending order using the sort_array function. The number you provide to this method indicates the direction of the sorting - 1 indicates ascending order, and -1 indicates descending order.
my_array = sort_array (my_array, 1);

That should be enough of a start for us to start using them in our code like with everything, the only way we ever really start to understand code is when we start to use it. So let's begin!

Fine Dining
So, now that we know how arrays work, it's time to apply this knowledge to Slicey Pete. For this, we're going to encounter a few more efuns, but ones that require us to understand the nature of arrays. 124

Epitaph Online

http://drakkos.co.uk

The first of these is the all_inventory efun, which gives all of the objects that are in a provided object (such as a room). To get the room in which an NPC is standing, we use the environment efun. To begin with, our function will look like this:
void eat_everything() { object room = environment (this_object()); object *contents = all_inventory (room); do_command (": looks around for something to eat."); }

this_object represents the object in which the code is found so it's a nice shorthand way of saying 'this object, this one right here, the one that this code is within'. The all_inventory efun gives us out an array of objects, and these objects are the entire contents of the room (including Pete himself). Let's do something with this information first, so we can see what we're working with:
void eat_everything() { object room = environment (this_object()); object *contents = all_inventory (room); object current; for (int i = 0; i < sizeof (contents); i++) { current = contents[i]; do_command ("' I see " + current->a_short()); } }

This is an important combination of topics here the real reason why loops are so powerful is that they are ideally suited to manipulating an array, as they are used here. Here, we use the counter variable of the loop to index elements in an array, in order. Bring him into a room with some things some NPCs, maybe, and call eat_everything on him:
> call Slicey Slicey Slicey Slicey eat_everything() Pete says: I see Pete says: I see Pete says: I see Pete says: I see pete you Captain Beefy Drakkos Wyrmstalker a stall

Okay so far the 'I see you' part refers to himself it's just a quirk of the a_short() call from his perspective, a_short on himself is 'you', just like if you call a_short() on yourself you'll see 'you' in return. Let's make Pete a little voracious to begin with we'll have him eat everything except players. We can tell when an object is a player, because it will return 1 to the efun interactive, so we can do the following:
void eat_everything() {

125

Epitaph Online object room = environment (this_object()); object *contents = all_inventory (room); object current; for (int i = 0; i < sizeof (contents); i++) { current = contents[i]; if (current == this_object()) { continue; }

http://drakkos.co.uk

if (!interactive (current)) { do_command ("' Get in ma belly, " + current->query_short() + "!"); do_command (": consumes " + current->the_short() + " with gusto!"); current->move ("/room/rubbish"); } } }

The first if condition inside our for loop checks to see if the current object in our array is Pete himself we don't want him to consume his own flesh. In the context of a loop, the continue statement means 'stop with this iteration of the loop and continue on with the next'. In a loop, a break (like we saw in the switch statement, way back when) means 'stop with this loop entirely'. Continue causes us to just skip the current iteration of the loop. The second if condition checks to see if the object is not interactive if it's not, Pete will eat it. It doesn't matter what it is, Pete will quite happily shove the object in his mouth:
> look You can hear: Nothing. In the distance you can see: Nothing. This is a skeleton room. It is a very cold winter afternoon with scattered puffy clouds and a steady breeze from the northeast. There are three obvious exits: north (clear), east (clear) and west (clear). Captain Beefy and Slicey Pete are standing here. > call eat_everything() pete Slicey Pete exclaims: Get in ma belly, Captain Beefy! Slicey Pete consumes Captain Beefy with gusto! Slicey Pete exclaims: Get in ma belly, stall! Slicey Pete consumes the stall with gusto!

Oh Pete, you so crazy!

The Foreach Structure


There exists a specific kind of programming structure designed purely to step over each element in an array it's called foreach, and you'll see it quite a lot as your read over existing code. It's just a neater way of doing what our loop above does we can dispense with manually pulling each item off of the array and let the code do it for us. A foreach loop starts at the beginning of the array, and ends at the end, and at each stage it moves on one element forwards through the array. 126

Epitaph Online

http://drakkos.co.uk

It's not good for when we need fine grained control over how we work with an array, or when we need to know how many iterations we have gone over, or when we need to know where in an array we currently are. However, for straight-forward stepping over an array, it would look like this:
foreach (object current in contents) { if (current == this_object()) { continue; } if (!interactive (current)) { do_command ("' Get in ma belly, " + current->query_short() + "!"); do_command (": consumes " + current->the_short() + " with gusto!"); current->move ("/room/rubbish"); } }

We use the current variable in the array syntax as a condition for the structure we don't need to manually query the size of the array or pull the variable off of the array. The loop will begin at the very first element, and pull that element off the array and store it in current. Next time around the loop, it will take the second element and put that into current, and so on until all of the elements of the array have been stepped over. This is a very lovely syntactic structure, and a feature that LPC had long before it made its way into more mainstream languages like Java. The sole drawback is that it doesn't give you access to a specific index number if you need that at any point in your code, you'll need to rely on a standard for loop You'll find that a combination of for and foreach will be required to make full use of arrays as you progress through your projects learning which is best for a particular task is something that will come with experience.

Conclusion
Arrays are a complicated topic, and in the next chapter we're going to look at some of the many varied cool things we can do now that we've added them to our programming toolbox. I'm not kidding when I say that this is what promotes you from the shallow end of the programming swimming pool without even talking about any other data types, we can do practically anything we'd ever need by relying purely on arrays. There's one more standard data type we're going to introduce in Introductory LPC, but our journey through introductory syntax is starting to come to a conclusion we've covered some scary territory over the past chapters and you are to be congratulated for reaching this far! Now, go practice arrays until your brain bleeds!

127

Epitaph Online

http://drakkos.co.uk

Hooray for Arrays


Introduction
In the last chapter we looked at the basics of using arrays in LPC that was a big step, and it opens up a lot of new things we can talk about. Arrays are embedded deeply in the mudlib, to the point that you can't do anything of consequence without working with them in one capacity or another. Most of the useful functions of the MUD return arrays rather than single bits of data, and in this chapter we're going to look at some of these. In the process, we'll add the last of the rooms to our village, adding in interactivity based on what a player has in their inventory, among other things.

A Secret to be Discovered
Look back at our map of Deadville there's one room we still haven't done, marked C on the map. It's connected to market_southeast, but we don't have anything for it yet. The room itself is going to be a normal room, except it will be absolutely full to the rafters of grues! We'll start with the usual first step, our skeleton implementation. We'll save this as grue_room.c:
#include "path.h"; inherit STD_ROOM; void setup() { set_short ("abandoned shop"); add_property ("determinate", ""); set_day_long ("The fingers of sunlight do not touch this occult " "place. Hope has long deserted this room.\n"); set_night_long ("In the darkness, it's possible to sense occult, " "eldritch energies seeking entry. Anyone in the room at " "night without the special magical safety item is likely " "to be eaten by a grue.\n"); set_light (100); add_day_item("occult place", "There's a definite sense of occultness " "in the air."); add_day_item (({"occult", "occultness"}), "Yes, that sort of thing."); add_night_item ("occult eldritch energies", "They are very oblong."); add_night_item ("special magical safety item", "Hoo-boy, you better hope " "you have that with you!"); room_day_chat ( ({ 120, 240, ({ "You are probably safe here, for the time being.", "This room makes you sad.",

128

Epitaph Online "You sense that the room doesn't want you to be here.", })})); room_night_chat ( ({ 120, 240, ({ "You better hope you have the magic item!", "I feel sorry for anyone who doesn't have that special item!", "#eaten_by_grue", })})); add_exit ("west", ROOMS + "market_southeast", "door"); } void eaten_by_grue() { // We'll fill in the code for this later. }

http://drakkos.co.uk

Note that one of the room_night_chat entries is to a function called eaten_by_grue. We'll come back that one soon, but we'll just add in a stub for it now. Stub methods are a great way to structure your code without needing you to have to actually code everything at once. It starts off simply enough, but now we're going to do some hard-core array manipulation, for reals. We're going to make it so that if anyone wanders into this room, at night, and doesn't have Beefy's ring on them well, bad stuff is going to happen.

A Grue Some Fate!


The criteria for being eaten by a grue is fairly simple if it's night-time and don't have a beefy diamond ring on you, you get eaten, arr num num num. This is a fate for everyone in the room, and so our function has to check, for every player and NPC in the room, if they have protection. Let that be a lesson, boys and girls don't go out without some protection on you. Let's start by just getting all the objects that are currently in the room. We do this using all_inventory. This will get everything in the room players, NPCs, and items that happen to be lying on the ground. It's exactly the same as we did for Slicey Pete in the last chapter, except we don't need to get an environment this_object is already an environment:
void eaten_by_grue() { object *room_contents; room_contents = all_inventory (this_object()); }

There are more compact ways to do the next few bits, but don't worry about those we're going for simple, easily comprehensible code here so as each step is clearly understandable. Once we have our list of objects, we want to get the ones that are classed by the MUD as living (NPCs and players). We use the living efun for this if we wanted players only, we'd use the interactive or userp efuns instead: 129

Epitaph Online

http://drakkos.co.uk

void eaten_by_grue() { object *room_contents; object *living_objects = ({ }); object ob; room_contents = all_inventory (this_object()); foreach (ob in room_contents) { if (living (ob)) { living_objects += ({ ob }); } } }

Now, we want to step over each of those living objects we've found, and detect whether they have the amulet. Let's slow down here and think about this incrementally how would we do it for one living object? Well, first we'd need to see whether that object had an amulet on them that's easy, that's what match_objects_for_existence does:
matches = match_objects_for_existence ("beefy ring", living_objects[0]);

The ring itself has a query_worn_by function that tells us who is wearing it, so we can use that. We need to check over each of the amulets returned by match_objects_for_existence, even we expect players to just have one. They may have two, and it would be a little unfair if they were eaten because we happened to check if they were wearing the wrong one. We're going to use a integer variable here called safe to determine if someone is to be eaten. If safe is 1, that individual doesn't get eaten. If it's 0, they do.
void eaten_by_grue() { object *room_contents; object *living_objects = ({ }); object ob; object *matches; int safe; room_contents = all_inventory (this_object()); foreach (ob in room_contents) { if (living (ob)) { living_objects += ({ ob }); } } matches = match_objects_for_existence ("beefy ring", living_objects[0]); foreach (object ring in matches) { if (ring->query_worn_by() == living_objects[0]) { safe = 1; } } }

130

Epitaph Online

http://drakkos.co.uk

We can assume at the end of this foreach that any player it is still dealing with is not protected from our vicious grues. We tell them they have been found wanting, and then unceremoniously slay them:
if (safe == 1) { tell_object (living_objects[0], "You are judged and found worthy!\n"); } else { tell_object (living_objects[0], "You are devoured by a grue!\n"); tell_room (this_object(), living_objects[0]->query_short() + " is devoured by a grue!\n", ({ living_objects[0] })); living_objects[0]->do_death(); }

There's a lot happening here, so let's look at it in context:


void eaten_by_grue() { object *room_contents; object *living_objects = ({ }); object ob; object *matches; int safe; room_contents = all_inventory (this_object()); foreach (ob in room_contents) { if (living (ob)) { living_objects += ({ ob }); } } matches = match_objects_for_existence ("beefy ring", living_objects[0]); foreach (object ring in matches) { if (ring->query_worn_by() == living_objects[0]) { safe = 1; } } if (safe == 1) { tell_object (living_objects[0], "You are judged and found worthy!\n"); } else { tell_object (living_objects[0], "You are devoured by a grue!\n"); tell_room (this_object(), living_objects[0]->query_short() + " is devoured by a grue!\n", ({ living_objects[0] })); living_objects[0]->do_death(); } }

Please make sure you understand each step and how all the variables relate to each other - it's quite complicated. This is easily the most complicated piece of code we've encountered thus far. However, it provides a baseline for some very common functionality. Note that this only works for one single individual in the room our last step is to do all of this in a foreach so it steps over each individual and performs the same checks: 131

Epitaph Online

http://drakkos.co.uk

foreach (ob in living_objects) { safe = 0; matches = match_objects_for_existence ("beefy ring", ob); foreach (object ring in matches) { if (ring->query_worn_by() == ob) { safe = 1; } } if (safe == 1) { tell_object (ob, "You are judged and found worthy!\n"); } else { tell_object (ob, "You are devoured by a grue!\n"); tell_room (this_object(), ob->query_short() + " is devoured by a grue!\n", ({ ob })); ob->do_death(); } }

The tell_room and tell_object functions are new to us they're what's used to send messages around the MUD. The first tells the player specifically what happened to them:
tell_object (ob, "You are judged and found worthy!\n");

The first parameter is the object to which we send the message, and the second is the message itself. Tell_room is a little more complicated it tells everyone in the specific container object (in this case, a room), except for those specified as being excluded. The first parameter is the room to send the message to. The second is the message itself, and the third is an array of objects that are not to see the message. We exclude the current object from the tell_room, because they already get a tell_object sent to them. Update the room and call eaten_by_grue to make sure it works try it while wearing the ring, and try it without. When you've tried it without, you'll notice something disappointing the death message is Very Dull:
[Drakkos had a very lucky escape -- killed by Nothing]

Jeez, what is the point of being eaten by a grue if you don't at least get to see an interesting death message? No point at all, I say... so let's make sure we have one. Death messages are based on a query_death_reason function defined in the object that is responsible for calling do_death. So let's add one of those:

132

Epitaph Online string query_death_reason() { return "being eaten by a grue"; }

http://drakkos.co.uk

This by itself is not enough, we also need to add the room to the individual's list of attackers so that the MUD knows what caused the death. We use the attack_by method for this, before we call do_death:
ob->attack_by (this_object()); ob->do_death();

And that, as they say, is it! Next time the grues find you wanting, the following death message will be triggered:
[Drakkos had a very lucky escape -- killed by being eaten by a grue]

That's much more entertaining, and that's what we're all here for.

Getting To Our Shop of Horror


At the moment our shop is inaccessible. We need to change that in our market_southeast file. To avoid people simply stumbling into the store with no warning, we're going to make entrance based on an exit function. First, we add the exit as normal to our room:
add_exit ("east", ROOMS + "grue_room", "door");

And then we modify that exit in this case, to indicate a function that is to be called when people attempt to take the exit:
modify_exit ("east", ({ "function", "warn_player" }));

The exit function will only be called if you are not currently sporting the demon property, so make sure you remove that before you attempt to write this part of the room. Whenever anyone tries to take this exit, the MUD will call the function warn_player. If that function returns a 1, the player is permitted to take the exit. If that function returns a 0, then they are stopped. A special word of warning don't use this_player() in an exit function if a player is following another player through an exit, only the first player will ever be returned from this_player(). Instead, use the parameters passed to the function by the MUD:
int warn_player (string direction, object ob, string special_mess) { }

The first parameter is the direction of the exit. The second is the object taking the 133

Epitaph Online

http://drakkos.co.uk

exit (you use this rather than this_player()), and the third is the message that will be displayed to the destination room. Usually we don't need to worry about either the first or third parameters, and can concentrate purely on the second. We're going to provide a warning here - when a player is warned, they get a timed property placed on them. If they attempt to take the exit while they have the property, they get let through:
int warn_player (string direction, object ob, string special_mess) { if (ob->query_property ("been warned about grues") == 1) { return 1; } tell_object (ob, "The room ahead smells of grues... are you sure you " "want to risk it?\n"); ob->add_property ("been warned about grues", 1, 120); return 0; }

However, when taking this exit for the first time, we get an ugly bit out output:
You open the east door. The room ahead smells of grues... are you sure you want to risk it? What?

That 'what?' is a consequence of the way the MUD deals with error messages. We want rid of that, so we need to add a little bit of black magic to our function - a notify_fail function, which can be used to override the normal 'try something else' error message as we see above. This is the only place you should use notify_fail without a very good reason if you don't know what those reasons might be, then you don't have one.
int warn_player (string direction, object ob, string special_mess) { if (ob->query_property ("been warned about grues") == 1) { return 1; } tell_object (ob, "The room ahead smells of grues... are you sure you " "want to risk it?\n"); ob->add_property ("been warned about grues", 1, 120); notify_fail (""); return 0; }

Notify_fail lets you change the fail message that appears, and in this case we simply suppress it entirely - we already give them the information they need as part of the tell_object. Update the room, and everything should fit together like a greased up jigsaw.

Tell Events
One of the things we have available on Epitaph is a more sophisticated method of providing information to objects and rooms it's the tell event system, and lets people set colours for the messages that the MUD sends. There are several 134

Epitaph Online

http://drakkos.co.uk

versions of this function, but the two you'll be most interested in are tell_object_event and tell_room_event they work much like the methods you've seen above, but also require an event category. These are defined in the header tell_event.h. This header file contains various 'tell event classes' the one we want for a message such as 'you have just been eaten by a grue' is EVENT_CLASS_BAD_THING. So we can change our eaten_by_grue function to honour player colour choices:
tell_object_event (ob, "You are devoured by a grue!\n", EVENT_CLASS_BAD_THING); tell_room_event (this_object(), ob->query_short() + " is devoured " "by a grue!\n", EVENT_CLASS_BAD_THING, ({ ob }));

We need to do the same thing in our warn_player function:


tell_object_event (ob, "The room ahead smells of grues... are you sure you " "want to risk it?\n", EVENT_CLASS_BAD_THING);

There is a very good reason why we use tell event classes like this it makes our game much more accessible if people can change the colours of messages by event, or even earmuff them entirely. If you are partially sighted and want to limit game output to a degree, tell events are our way of supporting that people can switch off room chats, NPC souls, weather, clan output, effects chatter, and lots more using this system.. Please, if you're ever sending info via tell_object or tell_room, use tell_object_event or tell_room_event instead.

Conclusion
Arrays have unlocked some truly powerful functionality for us, but the manipulation of arrays may still be confusing. All I can recommend is practice it doesn't get any more important than understanding how arrays work, and if you are not one hundred percent on how they work you should find code and read code and ask questions and shout and scream and stamp your feet until you are. I'm not even kidding! The only bit of syntax left for us in Introductory LPC one is the mapping, and that's the topic for the next chapter. You've already come so far there's not long left to go!

135

Epitaph Online

http://drakkos.co.uk

Mapping It Out
Introduction
The last data type we're going to explore as part of our introductory exposure to LPC is the mapping. It goes by different names in different programming languages, but if you ever hear people talking about an 'associative array' or a 'hashtable' or 'hashmap', it's this data type to which they are referring. Mappings are an internally complex data type, but you don't need to worry about any of that we just need to know how to manipulate them, and that's our topic for this section. So, let's not dilly dally, let's get stuck in!

The Mapping
Imagine you wanted a way to link together pieces of data. For example, if we wanted to store the average skill level and the name of players, we'd need two arrays one of strings containing names, and another of integers holding levels, like so:
int *average_levels = ({ }); string *player_names = ({ });

Then, when we wanted to add a player's guild level, we'd add their name and level to the appropriate arrays:
player_names += ({ "drakkos" }); average_levels += ({ 1337 });

We'd need to do this each time that we wanted to add a new pairing of data: Index 0 1 2 player_names "drakkos" "hambone" "eek" average_levels 1337 1227 4444

A mapping is essentially a data structure that allows for that kind of relationship between elements. Writing your own association between arrays is time consuming and prone to problems when it comes to making sure everything gets manipulated at the right time at the right way in both arrays, and as soon as one mistake is made with this the entire set of data loses integrity. Using a mapping means that the MUD does all the Tedious Busy Work for you, and that's what we're looking to do as programmers offload tedious busy work onto the computer. 136

Epitaph Online

http://drakkos.co.uk

A mapping is declared thusly:


mapping my_map;

And set with an empty internal state using the mapping notation:
my_map = ([ ]);

Note that this is subtly different from an array it's square brackets within rounded brackets rather than braces within rounded brackets. If we want to setup a mapping with some starting values, we use the following:
my_map = ([ "drakkos" : 1337, "eek" : 4444, "hambone": 1227, ]);

Note that each of the pairs are separated by a colon rather than a comma. As with the array, we need to know how to manipulate a mapping before we can really make use of it, and before we do that we need to talk about the terminology of a mapping. Unlike an array, a mapping does not have an index by which it can refer to elements. Instead, a mapping has a key and value pairing. A particular key is linked to a particular value. Our example above, in a mapping, would look like this: Key "drakkos" "hambone" "eek" Value 1337 1227 4444

Thus, the key "drakkos" will have the value 1337, and the key "taffyd" will have the value 666. To put a value into the mapping, we use the following notation:
my_map ["<key>"] = <value>;

So, to add in a fourth entry:


my_map ["gruper"] = 69;

At this point, our mapping looks like this: Key "drakkos" "hambone" "eek" "gruper" Value 1337 1227 4444 69

137

Epitaph Online

http://drakkos.co.uk

To pull out a value, we use a similar notation:


int value; value = my_map["gruper"];

We don't need to know in what position of the mapping we need to look for values, because the mapping does that for us. Indeed, you can't guarantee that the order in which you add values has any relationship to how they are stored internally for all sorts of Complicated Technical Reasons. You have to think of it a bit like a bag of data rather than a list. This is an important point just because we added things in a particular order, that does not guarantee in the slightest that's the order they are stored in the mapping. To remove an element from the mapping, we use the map_delete function, like so:
map_delete (my_map, "eek");

This looks through our keys in the mapping match from the mapping: Key "drakkos" "hambone" "gruper"

for a match, and then removes that Value 1337 1227 69

Although syntactically simple, they are quite complicated conceptually so once again, we're going to use an example to illustrate how they actually work.

The Magic Hate Ball


We're going to add a new item to our marketplace a magic hate ball mounted on a plinth. This will be in market_southwest:
#include "path.h" inherit STD_OUTSIDE; int do_hate(); void setup() { set_short ("southwest corner of the marketplace"); add_property ("determinate", "the "); set_day_long ("This is the northeast corner of the marketplace. " "A magic hate ball is mounted on a plinth, attached by a chain. " "Sunlight glints on its malevolent surface.\n"); set_night_long ("This is the northeast corner of the marketplace. " "A magic hate ball is mounted on a plinth, attached by a chain. " "Starlight glints on its malevolent surface.\n"); add_item ("magic hate ball", ({ "long", "The magic hate ball gives wisdom when you shake it!", "shake", (: do_hate :), })); set_light (100);

138

Epitaph Online

http://drakkos.co.uk

// Setup stuff as before } int do_hate() { return 1; }

Note that we're using the function pointer notation for the add_item we need to use that when we attach functions to verbs in add_items. In Introductory LPC 2 it will become clear why, but for now you're going to have to accept that sometimes you can use the # notation, and sometimes you need to use the function pointer notation. What we're going to get the magic hate ball to do is give people who shake it a venomous message, and if they shake it again it repeats the same message. Thus, everyone gets a random message the first time they shake it, but the ball is consistent with its advice. We need a class-wide variable for this so that it gets stored between executions of our function:
mapping previous_responses = ([ ]);

In our function, we first need an array of possible responses:


string *random_responses = ({ "Shut up and die.", "I slept with your wife.", "Suicide is your only option.", "Everyone hates you.", "You suck.", });

Back to the idea of incremental development let's just follow through the process of someone getting a response for the first time. We select a random response, and display it:
int do_hate() { string response; string *random_responses = ({ "Shut up and die.", "I slept with your wife.", "Suicide is your only option.", "Everyone hates you.", "You suck.", }); response = element_of (random_responses); tell_object (this_player(), "The magic hate ball says: " + response + "\n"); return 1; }

To actually use our mapping, we need to start setting and querying values in it. First, we need a key this will be the name of the player. We declare a string variable called player at the top of our function, and then set it to be whatever the 139

Epitaph Online

http://drakkos.co.uk

player's name is:


player = this_player()->query_name();

Before we get to generating a response, we need to check to see if there's been a previous response by querying our mapping with the key we just acquired:
response = previous_responses[player];

The response we then give to the player is dependant on whether there is anything in this variable, like so: Putting that all together gives us the following:
int do_hate() { string response; string player; string *random_responses = ({ "Shut up and die.", "I slept with your wife.", "Suicide is your only option.", "Everyone hates you.", "You suck.", }); player = this_player()->query_name(); response = previous_responses[player]; if (!response) { response = element_of (random_responses); previous_responses[player] = response; tell_object (this_player(), "The magic hate ball says: " + response + "\n"); } else { tell_object (this_player(), "The magic hate ball says: "told you: " + response + "\n"); } return 1; } I already "

Let's step through that process from the perspective of a player. Player comes along and says 'Hey, cool eight ball. I am going to shake it 'till I break it!'. They shake the ball, and the first thing the function does, after setting up the random responses, is to get the player's name, which is "drakkos", and then query our mapping to see if the key "drakkos" has a value associated with it. Our mapping though is completely empty. It's empty, so no response is to be found. The code then picks a random response from our list, let's say it's "You suck", and then puts that value into the mapping associated with the key of the player name:

140

Epitaph Online

http://drakkos.co.uk

Key "drakkos" It then prints out our message:


> shake ball The magic hate ball says: You suck. You shake the magic hate ball.

Value "You suck."

'My word!', exclaims Drakkos. 'That is rather rude, and no mistake! Why, I shall shake this little rascal again to see what it has to say!" Drakkos shakes the ball again this time, when it comes time to query the mapping, there's already a response stored. That gets stored in the variable 'response', and when we get to the if statement response is no longer empty and so the else clause is executed:
> shake ball The magic hate ball says: I already told you: You suck. You shake the magic hate ball.

Saddened and appalled by this inhuman criticism, Drakkos wanders off into a nearby room, and ends up eaten by a grue. This area, he decides, was not written by a nice person. Why so mean, magic hate ball? Why so mean?

More Mapping Manipulation, Matey


There are three further things we might like to do with a mapping in order to manipulate it effectively. One is to get a list of all the keys in a mapping we can do that with the keys function. It returns it as an array:
string *names; names = keys (my_map);

This will give us everything in the left hand column of the mapping. Going back to our example mapping: Key "drakkos" "hambone" "eek" "gruper" Value 1337 1227 4444 69

Getting the keys of this will return the following array:


({"drakkos", "hambone", "eek", "gruper"})

We cannot guarantee that the elements will be returned in that particular order, 141

Epitaph Online

http://drakkos.co.uk

but all of those elements will be present. Another thing we might like to be able to do is get all of the values. This is done in a similar way, using the values function:
int *levels; levels= values (my_map);

This will return the following array from our example mapping:
({ 1337, 1227,4444, 69 })

Again, in no guaranteed order. If we want to enforce some kind of order on this, the we must handle it ourselves. Finally, we may wish to step over each of the key and value pairings in turn, performing some kind of operation. We can do that longhand like so:
string *keys; string key, value; keys = keys (previous_responses); foreach (key in keys) { value = previous_responses[key]; tell_object (this_player(), key + " was told '" + value + "'\n"); }

However, LPC is nice enough to give us a foreach structure purely for handling mappings! The above code rolls into a simple foreach, like so:
string *keys; string key, value; foreach (string key, string value in previous_responses) { tell_object (this_player(), key + " was told '" + value + "'\n"); }

Let's try that out by adding a 'consult' option to our ball:


add_item ("magic hate ball", ({ "long", "The magic hate ball gives wisdom when you shake it!", "shake", (: do_hate :), "consult", (: do_consult :), }));

We'll need a function prototype at the top of the code:


int do_hate(); int do_consult();

And folding in the code we just discussed:

142

Epitaph Online

http://drakkos.co.uk

int do_consult() { string key, value; foreach (key,value in previous_responses) { tell_object (this_player(), key + " was told '" + value + "'\n"); } return 1; }

Now we can consult to see what everyone has been told by the hate ball:
> consult ball Drakkos was told 'I slept with your wife.' You consult the magic hate ball.

Alas though, if no-one has been told anything, we get a fairly uninspiring message:
> consult ball You consult the magic hate ball.

We should put a check to ensure that if the mapping is empty, we get the appropriate message. Luckily sizeof works for mappings as well it gives us the number of keys in a particular mapping, so we can fix that quite easily:
int do_consult() { string key, value; if (sizeof (previous_responses) == 0) { tell_object (this_player(), "No-one was told anything. Now curl up " "and die.\n"); } else { foreach (key,value in previous_responses) { tell_object (this_player(), key + " was told '" + value + "'\n"); } } return 1; }

Now we get a much better experience when we try to consult an empty hate ball:
> consult ball No-one was told anything. Now curl up and die. You consult the magic hate ball.

Thanks for the update, magic hate ball!

143

Epitaph Online

http://drakkos.co.uk

Conclusion
It's unlikely you're going to need to do much with mappings at this stage of your creator career they are usually a part of more complicated functionality. It's important though that you understand what they are and how they are manipulated you'll likely encounter them quite a lot as you read through example code. They are quite unspeakably useful so useful in fact that when working in an environment where you don't have easy access to them, you miss them with a primal yearning. Luckily that's not a problem for us on Epitaph, for we are well supported as far as our mapping requirements are concerned, thanks to the support built into the driver and mudlib.

144

Epitaph Online

http://drakkos.co.uk

Debugging
Introduction
Nobody writes flawless code. That is as true of beginners as it is of people who have been coding for decades. Developing software is a constant battle against bugs, logic errors and the endless grind of maintenance. The skills that go along with fixing broken code are tremendously important to a creator, as is the mindset that allows you to be the kind of person who will sit down for hours to track down a particular bug. It makes you a very strange person mind, but it's that mindset that separates someone who will eventually be a self-sufficient creator and someone who will always need someone there to fix their problems. You should very much aim to be the former, because there will be vanishingly few people who will be willing to do the latter.

The Two Different Kinds of Errors


The errors you will be most familiar with are syntax errors those horrible bits of red text that flash up when you update a piece of code that won't work. Those are the most common errors you encounter, and while it may seem unlikely at the moment, they're also the ones that will stop being a problem for you to solve on your own. After a while, you'll start to appreciate what the errors are telling you and how to fix them. The first mistake novices make when dealing with syntax errors is to not read the error. They see the flash of red, decide 'my code is broken', and then never make the mental connection between one and the other. The error messages in FluffOS are a lot better than they are in many languages, and they always contain within them the information you need to resolve the error. We'll come back to that later. Getting an object loaded is only part of the struggle the next part is making sure it actually works. When your object doesn't do what it's supposed to, it's called a logic error. It's not a syntax problem (if it was, you wouldn't be able to load the object at all), it's a problem with what you have told the code to do. Those kind of errors never go away, but there are ways you can track them down, and we'll talk about them later in this chapter. The problem with a logic error is that it may not be obvious it may only present itself once in every ten times you perform a task, or maybe only if you have a certain item in your inventory, or perhaps only at six am on a Tuesday. The only way you can have confidence that your object is working the way it is supposed to is to test it. We'll talk more about that in a bit.

Fixing your Syntax Errors


Seriously, read the error it contains all the information you need to fix it. In 145

Epitaph Online

http://drakkos.co.uk

some odd cases, the error message might not be entirely transparent, but each of the words it gives you is there for a reason. Let's look at a broken version of Beefy:
#include <npc.h> inherit STD_MONSTER; void setup() { set_name ("beefy"); set_short ("Captain Beefy"); add_property ("determinate", ""); add_property ("unique", 1); add_adjective ("captain"); add_alias ("captain"); set_gender (1); set_long ("This is Captain Beefy, a former officer of Her Majesty's " "Secret Service. Once he was a pretty impressive guy, with a large " "string of accomplishments to his name. Now, he's just another " "survivor in the grim darkness of the LPC apocalypse.\n"); basic_setup ("human", "survivor", MONSTER_LEVEL_NORMAL); setup_nationality(MUDLIB_NATIONALITIES + "british", "scotland"); load_chat(10, ({ 2, "' Please don't hurt me.", 1, "@cower", 1, "' Here come the drums!", 1, ": stares into space.", 1, "' Oh, hello there $lcname$.", })); load_a_chat(10, ({ 1, "' Oh please, not again!", 1, "' I don't get medical insurance!", 1, "@weep", })); set_challenge_profiles (([ PROFILE_SHARP_WEAPONS : CHALLENGE_INCREDIBLY_LOW, PROFILE_PIERCE_WEAPONS : CHALLENGE_INCREDIBLY_LOW, PROFILE_BLUNT_WEAPONS : CHALLENGE_INCREDIBLY_LOW, PROFILE_UNARMED : CHALLENGE_INCREDIBLY_LOW, ])); set_greet_response ("' Oh, I hope you are not here to kill me. "hello."); set_unknown_response ("' I don't know anything about that."); add_response ("murder", "' Please don't kill me!"); add_response ("kill", "' No, please! God, please don't kill me!"); dress_me(); request_item ("plain bra", 100 init_equip(); } I mean, "

146

Epitaph Online

http://drakkos.co.uk

Trying to update this NPC gives us the following set of syntax errors:
> update /d/support/deadville/debugging/broken_beefy.c /d/support/deadville/debugging/broken_beefy.c line 39: 'PROFILE_SHARP_WEAPONS' before CHALLENGE_INCREDIBL /d/support/deadville/debugging/broken_beefy.c line 39: 'CHALLENGE_INCREDIBLY_LOW' before the end of line /d/support/deadville/debugging/broken_beefy.c line 40: 'PROFILE_PIERCE_WEAPONS' before CHALLENGE_INCREDIBL /d/support/deadville/debugging/broken_beefy.c line 41: 'PROFILE_BLUNT_WEAPONS' before CHALLENGE_INCREDIBL /d/support/deadville/debugging/broken_beefy.c line 42: 'PROFILE_UNARMED' before CHALLENGE_INCREDIBL /d/support/deadville/debugging/broken_beefy.c line 56: (); Undefined variable Undefined variable Undefined variable Undefined variable Undefined variable syntax error before

You can check the file at /d/support/deadville/debugging/broken_beefy.c to confirm the line numbers link to the lines of code I provide. Let's look at the first error, and analyze it:
/d/support/deadville/debugging/broken_beefy.c line 39: Undefined variable 'PROFILE_SHARP_WEAPONS' before CHALLENGE_INCREDIBL

The error says 'undefined variable', which seems pretty self-explanatory it thinks we're trying to use a variable that we haven't defined. It thinks that variable is called PROFILE_SHARP_WEAPONS, and it tells us line 39 which is the one marked in bold below:
set_challenge_profiles (([ PROFILE_SHARP_WEAPONS : CHALLENGE_INCREDIBLY_LOW, PROFILE_PIERCE_WEAPONS : CHALLENGE_INCREDIBLY_LOW, PROFILE_BLUNT_WEAPONS : CHALLENGE_INCREDIBLY_LOW, PROFILE_UNARMED : CHALLENGE_INCREDIBLY_LOW, ]));

Now, we know that PROFILE_SHARP_WEAPONS is a define in #challenges.h, because that's what we learned in that chapter. However, it is complaining that it doesn't know that token. It is actually telling us what the problem is it doesn't recognise PROFILE_SHARP_WEAPONS. There are two likely causes here we mistyped the token we need to use, or the #define is missing. We know it's not the former (because we know what the token should be), so it must be the latter. Sure enough, if you look at the top of our beefy file, there is no include for <challenges.h> If we add that to the top and update, you'll see we get a pair of new errors:
/d/support/deadville/debugging/broken_beefy.c line 40: Undefined variable 'CHALLENGE_INCREDIBLY_LOW' before the end of line /d/support/deadville/debugging/broken_beefy.c line 57: syntax error before ();

We have just seen that first little rascal of an error in our last fix, and we just #included the right file. So it's not that it can't find the define, it's that we've mistyped it. Reading that include file reveals that the defines we need look like 147

Epitaph Online

http://drakkos.co.uk

this:
#define #define CHALLENGE_EXTREMELY_EASY CHALLENGE_INCREDIBLY_EASY 0 1

So, we don't use CHALLENGE_INCREDIBLY_LOW we use CHALLENGE_INCREDIBLY_EASY. Change that, and we end up with a single error to fix:
/d/support/deadville/debugging/broken_beefy.c line 57: syntax error before ();

'Syntax error' doesn't sound helpful, but what it's saying is 'somewhere around line 57, your code is not well formed'. It can't be more precise than that, and sometimes the actual error won't be on that line at all (it can only tell us when it realises there is an error, not where that error actually is). Line 57 is the bold line though:
request_item ("plain bra", 100 init_equip();

The error in this case is not actually on line 57 instead it's on the line above. It's missing a closing bracket and brace. Common syntax errors include missing semi colons, missing brackets, missing braces, or extra braces. Basically, the error tells you 'read that code a bit better you've forgotten something'. The actual syntax errors will get easier to spot as time goes by, but the important thing is to read the information. Everything you need to know is there!

Logic Errors
Logic errors are the tricky ones they're the errors you get when your code doesn't do what you think it does. They can be hugely obvious (you clone an NPC, it says 'DOES NOT COMPUTE' and then your monitor bursts into flames), or tremendously subtle (if I eat this NPC when I am sitting on a chair, then ten seconds later I will get a runtime error). The only way you can really find them is to exhaustively test your object, actually trying to make it break. There are all sorts of formal ways in which testing is done, but I won't go over them here because they are very dull, and I have written two and a half million variations of the explanations behind them over my lifetime. I can't do it again. I. Just. Can't. You can Google white box testing and black box testing if you want to know more. A good test is one that exposes a flaw in your code. Make no mistake the players are going to find all the problems in your code as time goes by. If you're lucky, the errors won't be abuseable and the players will be generous enough to report the problem for you to fix. That doesn't excuse you your duty to do first pass checking to make sure it all works properly though.

148

Epitaph Online

http://drakkos.co.uk

First of all, make sure that your core object functionality works the way it's supposed to try it out a few times. Look at our magic hate ball that's an example of functionality that needs to be properly tested. Try it out yourself a few times. Try it out with a test character. Try watching your test character use it. Make your test character watch you use it. Make sure it resets properly. Think of all the possible ways in which a player can legitimately interact with your code, and then test all of them. Then, think of all the ways in which a player can illegitimately interact with your code. Try using invalid syntax, try referencing to objects other than the ones that are expected. Put yourself in the role of the smarmy bastard who is trying to prove how stupid you are for not anticipating that the magic hate-ball doesn't work properly with a video recorder. Become that guy (or gal). Then, when you've done that, get someone else to try to break your code. You are the worst person to test your own code beyond a certain point, because subconsciously you already know where the code is a little shaky, and unless you are tremendously disciplined you will find yourself entirely unconsciously directing your testing away from the weak areas. Ideally, you will find someone who hates you to test your code, but who could hate you with your cheeky little smile and beautiful eyes? Nobody, that's who. Only once you're pretty sure that the code works the way it's supposed to work should you then turn your work over to our playtesters their job isn't to uncover all the obvious bugs in your code, it's their role to explore your code from a (mostly) player perspective. You should make sure that the code is as robust as you can make it before it reaches them.

I found an error!
Good! Remember, all code has errors in it (the industry average is about 15 to 50 errors per 1000 lines of code. Yikes!) . It's better you know what those errors are because that way you can fix them. That brings us to the next of your tasks as a software developer trying to ensure robust code finding out why your code misbehaves. Much like finding errors, sometimes this is easy maybe it was a typo that didn't result in a syntax error, such as:
set_challenge_profiles (([ "warior" : CHALLENGE_INCREDIBLY_EASY, ]));

When you consider Beefy or check out his skills, you'll see they aren't what they should be that leads you instantly to the challenge profiles, and you can make some reasonable guesses as to what the problem might be and fix it. Sometimes though, the cause is far more subtle, and you're going to need to spend some time working it out. I'm afraid there's no shortcut there you're either the person who can bring yourself to spend four hours tracking down an obscure bug, or you're not. 149

Epitaph Online

http://drakkos.co.uk

In all cases, the process for dealing with logic errors you can't find is five-fold. Reproduce your Errors The worst errors are the ones that are intermittent. There is, deep in the mudlib, an error that sometimes shows up where the parser will cut off the first two letters of a string. 'look chair' will return the error 'cannot find 'air', no match'. The reason that bug still exists is no-one has found a way to reliably reproduce the damn thing. If you can't reliably reproduce your error, no amount of code-fu is going to save you. You can make 'best guesses' and add some general 'band-aid' solution that stops the error occurring, but at best that is an ugly hack. If your program has an off by one error that you cannot reliably reproduce, you can fix it by checking to see when a value is off by one and correcting it. The problem is you haven't fixed the error, just the symptom. Spend time working out what causes the error here is where an effective testing strategy can be a life-saver. You need to log what you've tried, and use that information to rule out possible problems. Don't just stare at the code, start drilling into it. Use Debugging Messages. A Lot. To find the cause of an error, you need to understand what Is happening inside your code. Because we have no real formal debugging software for LPC, we need to do it the Old School way stuff your code full of informs that tell you what is happening within. There is a lovely function that will help you with this it's called tell_creator:
tell_creator ("drakkos", "What up, homes!\n");

You can profitably use this within your code to see the value of each of the variables to make sure they are what you're expecting. Tell_creator is actually a very powerful function, and lets us slot all sorts of things into its eager parameter slots:
tell_creator ("drakkos", "What up, homes: %s!\n", "bing");

Within tell_creator, you can use a number of arguments which get slotted, in order, into the parts of the string you identify with control codes. %s indicates that ' a string goes in here', so when the tell_creator triggers I will see:
What up homes: bing!

You can use %d for whole numbers, %f for real numbers, and the tremendously versatile %O for everything else (including objects). Fill your code full of these messages to make sure that what is happening is what you actually thing is happening.

150

Epitaph Online

http://drakkos.co.uk

Simplify, Simplify Much of the code in a particular function is window dressing, as far as solving an error goes. It's parameter handling, and consistency checking, and other such miscellaneous house-keeping. It's all absolutely vital, but not when we're trying to locate a bug. In fact, all it does is complicate our search. Simplifying our function can make the search much easier - comment out any of the code that doesn't relate to the error so your search is restricted to only relevant code. Then, as you eliminate particular lines as being innocent, you can comment them out and replace them with place-holder values to further reduce complexity. Try Things Once you've got your code down to the statements that are important, try changing things - if you have variables being set, set them with different values to see what happens. If you have counter loops, try changing the condition. Make a careful note of what effect each change has, and analyse those changes with reference to the flow of logic in your program to see how each change is affecting the execution. Understand! This is the important one - once you've found a bug, understand what is causing it. Don't give in to the temptation of simply fixing it or recasting the code so that it doesn't happen without learning from it. Partly this is simply good practise - you learn more from your mistakes than you ever do from your successes, but only if you take the effort to ensure that you understand why your errors occurred. However, it's also very important from a pragmatic point of view. If you have a loop that is being iterated over one more time than it should, and you can't see why, then you can fix it by changing the continuation part of the loop so that it simply iterates one less time. All fine and dandy, and you've fixed your immediate problem - but fixes like this have a tendency of fixing only a symptom and leaving a larger problem unaddressed. The value of your variable wasn't what you were expecting - if you are making a reference to that variable elsewhere, or basing a calculation on it then you're going to have problems there as well.

Conclusion
It's not fun, and it's not entertaining. It can be terribly frustrating and hateful, but it's a part of development writing code isn't enough, you have to write code that works. Nobody does this, of course but you get better at it over time as you start to learn what is easy to fix, what is hard to fix, and why one should be better than the other. Having the mindset to be able to focus for hours on solving a 151

Epitaph Online

http://drakkos.co.uk

single bug is an important part of being a coder it may seem like a terrible effort to reward ratio, but it's only when working your way through the complex flow of logic in a complex program that you can really exercise all the analytical skills that are needed to be able to developer the higher tier of LPC objects that can be created.

152

Epitaph Online

http://drakkos.co.uk

So Long
Introduction
You've done very well to get this far programming is not an easy task regardless of what anyone tells you. Perhaps the most frustrating thing that goes with learning how to do it is how easily some people 'get it'. It's unfortunate that not everyone finds it as easy as other people, but the willingness to persevere can trump that initial natural understanding. Having the personality to continue when it gets hard is what separates a programmer from a talented amateur. You may very well fall into the category of talented amateur yourself, in which case this should be a warning for you too everyone struggles at one point or another to get something working, and if it's been easy all the time then you need to be willing to dig deep and find the will to go on when things start to become difficult. Anyway, in this brief chapter we shall wrap up our initial foray into LPC and talk about what happens next.

The Past
Deadville isn't exactly what you'd call a finished area there are bits that are not described, and other bits that have no interesting functionality. That's okay because it's not for the eyes of players it's a project so that you can see the process by which an area can be built. There are bits of Deadville that are quite sophisticated, and if you've managed to keep up with the code then you can make use of variations of what we've talked about to great effect. Learning how to develop on Epitaph is a two-pronged problem. To begin with, you need to know how programming code works, and this is in itself an entire textbook of knowledge. This is a transferable skill the coding structures that you've learned as part of this introduction to LPC are transferable to most programming languages. Individuals interested in seeing how transferable their skills actually are invited to explore my Java textbooks at http://www.monkeys-atkeyboards.com Combined with the understanding of generic coding structures, you need to learn the specific code that is unique to Epitaph how to create rooms, NPCs and items. Then you need to learn how to link them up, and make them do interesting things. A good creator is not just a coder a good creator has an unusual blend of skills. A good creator can architect projects, write well, and produce robust code. A beginner can only be expected to master one of these to start with, but you need to be able to do all three before you can think of yourself as a well-rounded 153

Epitaph Online

http://drakkos.co.uk

creator. It's not an easy task. So, let's recap what you've learned so far, and provide some context... there's been a lot of content, and so you may be forgiven for having not really realized how much you've actually done. In terms of generic programming structures, you've learned: Variables and variable scope If and Else-if statements Switch statements Functions and function scope For and Foreach loops While loops Arrays Mappings Header files

A substantial subset of these are present in most modern programming languages, and understanding how these work in LPC is the first step to understanding how they work in other environments. The syntax may be different, but the concepts are identical. In terms of what you've learned about Epitaph development, we can add to that: Inside and outside rooms Day and night items, chats and long descriptions How to use the linker NPCs Searching items How to use the task mapper Virtual objects Modifying exits Faction shops Object matching with match_objects_for_existence

That's quite a list of accomplishments, and a well deserved pat on the back is deserved for you for having learned all of these. I won't say mastered, because there's always more to learn about all of these things, and only diligent exploration of the MUD will reveal the more obscure features of all the things we've discussed.

154

Epitaph Online

http://drakkos.co.uk

The Present
So, what happens now you've learned all of this? Well, you've undoubtedly been assigned a newbie project by your domain administration, and your focus should be on developing that to as high a standard as you can. There are also small projects available for those who want to apply their new found skills to a small, open project with the intention of it being slotted into an existing area of the game to add richness. Just contact me (Drakkos, if you hadn't realized) and we can chat about that. But more than simply your current projects, you should be exploring the Epitaph codebase. Certainly within your own domain, think of things you enjoyed while playing and hunt out the code that handled it. Read the code, and try to understand how it works. All coding is, to a greater or lesser degree, plagiarism you can gain huge amounts of understanding by just reading what has come before. The problem with this of course is that you don't necessarily know if the code that you find is 'good code'. Epitaph has the benefit of being a very new MUD, and we don't have decades of legacy code lying around serving as traps for the unwary. Nonetheless, our rapid pace of development means that even in our lifetime there is code that is already 'out of date'. If you wonder whether code is worth using as a template, then feel free to contact me for guidance. As far as exploring code outside your domain goes, you should definitely acquaint yourself with the code in /mudlib/ and /items/. For other domains, ask the domain lord if they mind you scrutinizing the code for inspiration. Hardly anyone will mind, but it's polite to ask. Talk to other creators, especially those who may be as new as you. Part of what makes being a creator so much fun is the social environment in which you function, and you should take full advantage of that. You should also feel free to talk to members of the Support domain, particularly the leader of the domain it's only by talking to people like you that we know what things you want to know about! Asking for information on a particular topic is as helpful to us as it (hopefully) is for you!

The Future
There will come a point, hopefully not too far in the future, when you feel you're ready to learn new things. We've only scratched the surface of the kind of things that get written on Epitaph, and there is exciting territory to come. From here, you can move on to Being A Better Creator, Working With Others, and Intermediate LPC. Introductory LPC is the major section in this substantial handbook. There are four of these sections - two of them focus on the softer-side of Epitaph creators, and the last is a more advanced guide to LPC development. You should familiarise yourself with all of them - there's a lot discussed across these texts. 155

Epitaph Online

http://drakkos.co.uk

It would be a good idea to at least finish your first domain project before you worry about expanding into this territory getting a feel for the complexity of development and the things that you wish you knew how to do will make the lessons in later versions of the texts more valuable.

Conclusion
That's us for now! You're done with Introductory LPC and you can emerge from the room in which you keep your computer... the bright sunlight may hurt at first, but that'll go away with time. We'll see you again soon though, oh yes. We'll see you soon...

156

Epitaph Online

http://drakkos.co.uk

Reader Exercises
Introduction
Now that weve reached the end of your introductory voyage through LPC, lets give you some homework to see how well youve learned your lessons. Full answers to (almost) all of these exercises are available, but please attempt them yourself first you learn more by trying than by reading what other people have done for you.

Exercises
Fill in the Blanks We havent done a lot of descriptive work for Deadville, focusing instead on the code we needed to build it. Youll learn a lot however from going over each of the rooms and filling in the blanks. Ensure each room has the following: A day and night long description Day and night add_items Day and night room chats

Importantly, make sure every noun in your long description and in your add_items is included. This is something people will look for in your rooms - believe me. There is no worked example for this, but if you go through this process and would like to volunteer it as an example for other people, then send it to me and we can make it so. Connect to your Workroom Having gone to the trouble of making this village, you should be able to admire it whenever you like it should connect into your workroom. However, youre also at some point hopefully going to progress onto Zombieville. Create a new room in your directory and call it access_point. Link this room to your workroom, and link Deadville to it too. Add an exit function to the Deadville exit that makes sure only creators can enter your development. Playing Dress Up Most of our NPCs are dressed by the NPC clother. You should improve this situation by making sure they are appropriately dressed with their own unique ensemble. Each NPC should have a shirt, a pair of trousers, underwear, socks, and a pair of shoes or boots. Make sure this is true of all of your NPCs, and that you 157

Epitaph Online

http://drakkos.co.uk

pick sensible instance of each as to befit their status. If no sensible item exists, make one. The Very Model of A Modern Major General Captain Beefy has no unique weapon, which is somewhat out of theme for a man of his military background. Using the same kind of system as you did for his ring and his boots, create a beefy_sword. It will be saved as a .wep file, and you should look at existing examples of virtual weapons (in /items/weapons/) to ensure you set all the right values. Once youve done this, give him a suitable scabbard (.sca), and make sure both of these are provided to him when he is setup. His sword should be cloned inside the scabbard, and he should be wearing the scabbard along with the rest of his outfit. Responsiveness While our NPCs have several responses built into them, it would be nice if they were more responsive generally. Add in code to each of them that makes them respond positively to being greeted, both as a say and as a soul. Additionally, make them respond to your name with an appropriate amount of fear and cowering awe. Make them all respond to the names of each other, with a little comment about who they are (those NPCs that already have such responses can be left alone). For example, if I say beefy they could say Oh, Beefy he was our first NPC!. Finally, when I ask where certain NPCs are, such as Where is Beefy?, they should tell me where I am likely to find them. You don't have to track them down in code, something like 'Stabby Joe is usually in his shop' will suffice. Leashing The code we have for loading NPCs has the impact of leashing them to a specific room if Beefy is found elsewhere in the village when after_reset is called, hell be magically transported somewhere else. Change this so that he (and all other relevant NPCs) get moved into the room only if they have no environment. To make sure that they cannot then wander outside Deadville, put a creator check on your exit from the village (in street_01). Climbing the Mountain One of the things that changing the move messages with modify_exit allows you to do is create a sense of context to moving. Change the path leading to the village square so that it simulates climbing up a mountain trail. For example:
You climb the winding trail to the northeast.

158

Epitaph Online

http://drakkos.co.uk

Similarly, when heading back down the trail to street_o1, have that described to the player also. Calm Yourself Modify our crazy Stabby so that he cools down over time after having been riled to anger. Additionally, make it so that he gives a set of tiered warnings to players based on how angry he actually is.

Send Suggestions
Do you have ideas for exercises that would be cool, or are you trying to do something new in Deadville and just cant make it work? Send them to me, Drakkos at drakkos@imaginary-realities.com, and we can look at incorporating them into this section

159

Epitaph Online

http://drakkos.co.uk

Section Two

Being a Better Creator


160

Epitaph Online

http://drakkos.co.uk

Welcome to the Learning Zone


Introduction
In this material we are going to look at the ancillary skills that go with being a creator not the technical skills particularly. Instead we're going to look at the other skills you're going to need to master. These are the softer' skills in that they don't require Code-Fu, but they do need a considerable understanding of how everything fits together and the impact one decision is likely to have on another. They also require a comprehensive understanding of how to put together games make no mistake, although Epitaph is a volunteer text environment, you are still a games developer. Hardly any games have the depth, complexity, and sheer size of Epitaph. There may be no flashy graphics, but it doesn't mean you don't need a considerably broad set of skills to add to and improve the gaming experience.

The Plan for This Course


It is expected that you will progress onto this material once you have completed Introductory LPC. You either have your own version of Deadville in your /w/ drive (which is recommended) or at least understand all of the code that has gone into the here's one I made earlier' versions available in /d/support/deadville. The more adventurous among you may have written your own variation of Deadville, which would be even better. In this course of material, we're going to plan out a second village not in the half-hearted way that we planned out Deadville (which was, after all, purely based on a map). We're going to consider all of the necessary things that our new village will have, and how they support the wider MUD context. We didn't even begin to consider that for Deadville because it just wasn't important Deadville was a demonstration of syntax, not a playable area. Our second village is going to be called Zombieville. It's going to have quests, an infrastructure, and all sorts of associated goodness. It's our job in this material to work out exactly of what that goodness is going to consist, and more importantly how that goodness can contribute to an overall plan for the area. What we are not going to do though is code any of it. None of it. Not a single line of code, not a skeleton of a room, nothing. Nada. Zip. That's for the section on Intermediate LPC. Breaking the coding from the design is a good strategy because it means we have to think about what we're doing before we start coding, and that's something that hardly any of us do often enough. First of all, let's talk a little bit about what we're actually trying to achieve with a 161

Epitaph Online

http://drakkos.co.uk

new area for Epitaph. As an opening gambit, let's talk about what defines a good feature of a game, and then look at the implications that has for designing an interesting area for the MUD.

What's In A Game?
What is it that makes one game more appealing than another game? What makes Game A better than Game B? Obviously the answer to this will change from person to person different people enjoy different elements to different degrees. A good game will provide something that appeals to everyone, at all levels of the game. Or, more realistically in a world of constraints, a good game will provide something that appeals to specific genres of gamers. Within Epitaph though, we have many genres of gamers we'll talk about that in a later section of this material. Essentially what all game design boils down to is the provision of interesting game decisions. All games, at their fundamental level, are about providing an individual with a set of choices between different possible outcomes. Those choices become interesting, from a psychological perspective, when the outcome of those decisions is sufficiently clouded in uncertainty that, at the time of choosing, there isn't an obviously correct choice. Imagine the choice between the following options: Die Stay alive

Providing a player with those options is not providing them with the chance to choose between interesting courses of action. There's an obvious right answer and an obvious wrong answer. On the other hand, let's consider a second pair of decisions: Leap the chasm in the hope you reach the other side Go back down the track and find a new way to go

Now the decision becomes more interesting it involves balancing the likely difficulty of the jump against the capability of your character, as well as against the likely rewards to come from safely making that jump. On the other hand, if you take the safe option you risk not finding another path to where you want to go. The outcome of your decision is not known at the time of making your choice. It may be obvious in hindsight, but it wasn't obvious at the time. An element of this uncertainty comes from the black box of the outcome in traditional board games this is often incorporated as the roll of the dice. Even in entirely deterministic games (such as chess) the same basic feature is present. In chess, there is no randomness, but you still don't know until your opponent has made their move (and usually, a good few moves after that) as to whether you made the right decision when playing your pieces. 162

Epitaph Online

http://drakkos.co.uk

However, randomness alone does not make a decision interesting, it just makes the outcome uncertain. To progress to an interesting decision it has to be possible for people to reduce the uncertainty implicit in their options by logical deduction. You should never be able to entirely remove uncertainty (which is why randomness helps), but you should be able to reduce it to a level that you can make an informed, albeit uncertain, choice between competing alternatives. This can be quite subtle for example, imagine a game of Russian Roulette coded with two schemes: When you pull the trigger, a dice is rolled to determine whether you died. If you roll a 1 in 6, then you die. When you pull the trigger, the chance of your death is dependant on how many times the trigger has previously been pulled. You chance of killing yourself thus increases every time the gun doesn't fire a shot.

In the first case, it's the equivalent of the barrel being spun after every pull of the trigger in this scheme you are just trusting to the odds. Randomness by itself is neither fun nor interesting, and it's certainly not challenging. The only decision here is whether the 1 in 6 chance of death is worth whatever reward is presented. In the second case, it's the equivalent of the barrel being spun once at the start and then successive pulls of the trigger advancing the chamber onwards. In the second scheme, by paying close attention to how other players interact with the game, you can decrease the size of the window of uncertainty and thus make strategic decisions about when to play and when to walk away from the table. If randomness reduces how interesting a decision is, one thing that removes it entirely is arbitrariness. Instant death as an unexpected and unknowable consequence of making a decision is an easy way to completely undermine the process of informed decision making. For example, standing before a door: Go through Stay where you are

If the door leads to certain death, without any hint, suggestion, or possible way of knowing in advance, then it's a bad game decision. We are going to return to the idea of insta-death later it's a sufficiently common transgression that we should spend some time talking about why it's bad from a game development perspective. No matter how different the games are, eventually it resolves down to the choices you make and the space of time you have to decide upon those choices. In a first person shooter, your choice may be to fire your gun, switch your weapon, leap out of range, or run behind a wall the fact that there is only a few instants for you to make those decisions doesn't mean that there are no decisions being made. On the other hand, decisions in a game of chess can take minutes to evaluate, but it's still the same core process. What we are trying to do then as game developers is to provide opportunities for people to make interesting decisions. We will have cause to return to this topic 163

Epitaph Online

http://drakkos.co.uk

throughout this material as it is the basis of good game design.

Design Philosophy of Areas


There are several types' of area that can be developed in an online environment such as ours. Almost every real' area is actually a blend of these, but will emphasise two or three types over the rest. The following is not an exhaustive list, but covers the common foci that are present: Killing zone Wealth zone Questing zone Immersion zone Infrastructure zone Exploration zone

A killing zone is self-explanatory it consists of, ideally, interesting NPCs who exist only to be slaughtered for their high experience yield. Killing zones need not cater purely to higher level players - there is scope for killing zones to support almost any level range. However, there is more mileage' to be gained out of medium and higher level killing zones progress through the levels often slows as an individual progresses, and as such a killing zone is viable for much longer for higher level players. Wealth zones are almost identical to killing zones, except that rather than being killed for experience the NPCs are killed for the cash they provide. It's a good idea to keep killing zones and wealth zones as separated as possible so that a choice has to be made between pursuing wealth or experience. If one zone provides both, then it obsoletes those zones that provide only one or the other it's not an interesting game decision to kill in an area that rewards wealth and experience as opposed to an area that rewards just wealth or just XP. Questing zones exist to provide opportunities for problem solving. Quests on Epitaph are far more idiosyncratic than in most online games, and the type of player who enjoys solving puzzles is often greatly attracted to our conception of quest design. Players expect that a new area will introduce new quests into the game, and meeting that expectation is beneficial to everyone. Obviously our system is not perfect and badly designed and developed quests enhance the experience for no-one. We will spend quite some time discussing quest design. An immersion zone caters primarily to role-players and social gamers. It's an area to interact with, but not to gain any tangible in-game benefit. It may be characterised by a particular kind of ambiance, opportunities for social interaction, the availability of a range of equipment suitable for RP purposes, or sustainable opportunities for NPC interaction. They may also stitch into larger and more encompassing systems, such as our player housing system or player-run newspapers. Infrastructure zones exist to provide the tools and equipment that people need to 164

Epitaph Online

http://drakkos.co.uk

function in the game. They consist of things like the guild areas, smithies, weapon shops, armour shops, and places within which people can buy spell components and other necessities. Finally, an exploration zone is one that rewards players for paying attention. Areas replete with hidden items, secret areas and rewards for observation fall into this category. Every good area will have elements of each of these even designated killing' areas come with immersive room descriptions and occasionally even quests. Larger areas, like cities, will be defined by the blend of their sub-areas Dunglen for example has its quest zones (such as the university), its killing zones (the instances, mainly), its infrastructure zones (the Winchester and the various faction facilities), it's immersion zones (the safe zone) and its exploration areas (the rest of the city). Blended together, Dunglen has a particular feel based on the relative emphasis of its various constituent parts. The first step then in planning out your development is to decide on what kind of features you want to emphasise. This will be decided usually on the basis of what's needed in the larger context of your development if it's a street or district within a city, what features are currently not present that should be? If it's an area within a larger geographic zone, what does it need to provide in contrast to what's already available? You only rarely have complete autonomy over this decision there is always a process of give and take between what you'd like to develop and what needs to be developed. Discussions with your domain administration are the first step to gaining an appreciation for how the different developments within a domain are linked together.

Zombieville
So, with that in mind, let's decide on a focus for Zombieville. Our focus here is simplified by the fact that we don't need to fit our development into a larger domain plan we are free to choose which focus best fits our particular needs. Specifically, our needs are that the village be sufficiently involved to serve as a driver for the new syntax of LPC that we will encounter in Intermediate LPC. We have no player-base to satisfy here, after all. Killing zones do not give a lot of scope for this, although there is certainly much to be said for designing interesting and challenging NPCs. We'll leave that off as a focus though, and return to it in a later text since making interesting NPCs involves not so much new syntax as an introduction to the basics of a new concept artificial intelligence (we will get to that, just not now). Those who are interested in learning more of this topic should come speak to me for some suggested reading material. Likewise Zombieville is not going to be a wealth zone we're not going to learn much by filling it full of NPCs laden with expensive items, or by providing 165

Epitaph Online

http://drakkos.co.uk

scavenge zones full of valuable equipment. We also have no need to provide an infrastructure for anything else, so we won't have that as a focus either. We'll still talk about infrastructure in the course of this material, but it won't be something we adopt for Zombieville. Primarily, Zombieville is going to be a questing zone we're going to have a number of quests in this area so as to explore some deeper issues of game development and NPC interaction. We also want to have the area be immersive and responsive to player exploration so as to provide us an opportunity for incorporating some deeper concepts of LPC such as handlers and inherits. Thus, we pick three focii for our area: quests, immersion, and exploration. Having decided upon a focus, we can start to consider what we're going to include in the area to meet the needs of our development. That's where the real planning begins. It's a lot of fun to let your imagination run wild before coming face to face with the crushing reality of actually having to implement it all!

Conclusion
We're easing ourselves in gently here. Deciding on a focus for our area means that we can ensure we include the right kind of things to meet our goal. What that focus is going to be is usually a function of all the other areas in the domain (and indeed, the MUD as a whole). All of these areas appeal to different people in different ways, and a good game environment has examples of all of them in proportions that fit the makeup of the playerbase. Good games are engines for generating branching points between interesting choices. Bad games are just engines for generating choices. In order for a choice to be interesting, each of the outcomes must have an element of uncertainty, and that uncertainty should be manageable (but not eliminated) by player strategising. Bad games introduce randomness rather than challenge, and arbitrariness instead of strategy. Over the course of this document, it's my hope that together we can explore how to do Good rather than Bad in the code that we develop.

166

Epitaph Online

http://drakkos.co.uk

Player Demographics
Introduction
There are many, varied opportunities for gameplay to be found within Epitaph, and as such our playerbase has a blend of individuals who seek enjoyment in different spheres of the game. Before we can hope to provide opportunities for interesting decisions, we have to first know what kind of decisions our players find interesting. While there currently exists no mud-wide survey of playing styles, the work of Richard Bartle can help clarify the generic subgroups that are usually found within the demographics of an online MUD or MMORPG community.

Bartle's Definitions
Bartle defined four key sub-groupings of activities - while everyone is a blend of preferences for these, individuals tend to favour one over the others, and will seek out those activities on a more sustained basis. These sub-groupings are: Achievement Exploration Socialisation Imposition

Achievers are interested in setting and reaching in-game goals. These goals may be influenced by the game environment, or may be entirely personal. Although not an inextricable connection, achievers are often highly motivated by opportunities to accrue reputation within their gaming environment. Achievers like to be known as people who have achieved things. Explorers are interested in defining the boundaries of the game world, be these topologically related (mapping, for example), or how the game world models its own physics and reality. Gaining an understanding of the environment in which they function is what most defines an explorer. Socialisers play the game for its social context - the game itself is largely an engine for generating interaction opportunities between themselves and other players. Talker channels, internal bulletin boards and in game social environments are the stamping ground of a socialiser. Finally, there are the imposers, who play the game for the thrill of direct player to player conflict. These are the player killers who get a thrill from beating another human being in the way that they do not from beating a computer algorithm. Any opportunities that increase the conflict between individuals is what works best for 167

Epitaph Online

http://drakkos.co.uk

an imposer. More colloquially, imposers are best described by the label 'killers' the game world in general is a vehicle for attaining the necessary skills needed to be able to survive in the world of player killing. I must stress again that very few players are the caricatures presented here, and everyone combines aspects of these to a particular degree - but over the long term, a particular player will tend to focus on one (or at most two) of these over the others. Additionally, players who focus on a particular feature of the game tend to view unrelated developments through a somewhat nuanced eye. See the Richard Bartle paper for a full discussion of this topic.

Your Zone's Players


Understanding the focus of your area makes it easier to understand the kind of players who are going to be most attracted to it: Type of Zone Killing zone Wealth zone Questing zone Immersion Zone Exploration zone Player Type Achiever, Imposer Achiever, Imposer Achiever, Explorer Socialisers Explorers

It may seem like socialisers are ill served by our zone categories, but that's misleading - socialisers mostly gain their in-game fulfilment from other people rather than from what we provide as creators. We provide the environment in which their own interactions can set the parameters of the game. Likewise with imposers - although they will often frequent killing and wealth generation areas, it's usually as a means to an end - to gain the necessary power and wealth to be able to pursue and prevail in conflict with other imposers. Knowing what kinds of players are going to be drawn to our area gives us the necessary information to decide what features our area needs in order to best service their expectations. Every decision you make in putting together your design can either enhance the experience for your player groupings, or detract from it.

Catering For Achievers


Achievers like to have goals that they can set for themselves, as well as opportunities for developing a reputation within the game. Your task then becomes to provide mechanisms to support those goals. Achievers are looking to progress through the levels to become a Person of Power, and also a person of Repute. Systems that facilitate this goal are highly popular. Providing a mechanism by which points can be accrued is a useful way of 168

Epitaph Online

http://drakkos.co.uk

facilitating achievers, especially if those points are available for inspection by anyone else. Epitaph uses a range of 'Top Twenty Tables' which are available to anyone who may be interested. On these tables, players can compare their standing against the standing of others in a range of categories, such as skill levels, rating, achievement points, quest points, and so on. Essentially anything that adds a permanent, or semi-permanent mention of an individual is likely to appeal to an achiever. The 'first achieved by' tag on an achievement for example is a way of gaining a measure of MUD immortality. Additionally, since achievers often look to excel within specific sphere of the game, and since our game is, like most games of this type, based on invested experience, achievers are drawn to areas which offer significant opportunity for strengthening individual power. NPCs are an obvious example of this - both high XP and high wealth yields will draw achievers to a zone. More than this though, a degree of challenge is an important motivating factor achievers set goals for themselves, and often feel less satisfied when those goals are easy to achieve partially as a result of the achievement being available to more individuals. If an achiever is to stand out, their actions must separate them from less capable players. There is a substantial difference to providing ten easy NPCs that offer a small XP yield to providing one difficult NPC that has a large XP yield. Killing a boss NPC for example is a mark of achievement that stands out. As a corollary to this though, achievers are often highly focused on the cost/benefit ratio of their actions. While more powerful NPCs may give a welcome degree of challenge, they will often be ignored in favour of the more lucrative opportunities that come from killing many weaker NPCs in the same time scale. In the end, it's all about what gives the most benefit to the player. Opportunities for advancing seldom-developed skills can draw in achievers looking to maximise their potential, especially if those skills are operationally useful. While individuals may not flock to an area that offers a chance to increase their lute playing, having a TM chance for an important skill like perception can be a draw. Overall, achievers are largely about how things improve their characters or their personal reputation, rather than the satisfaction of doing the thing itself. As such, achievers tend to prefer high-yield, straight-forward quests to intricate, involved quests. For an achiever, choosing to do a quest is often a decision reached after weighing the potential reward against the possible XP/money per hour of another activity. Achievers essentially are pitting themselves against the game - their competition against other players is indirect. It's based on accumulated wealth, or number of quest points, or level in a skill, or teaching bonuses. Thus, in order to appease an achiever, these are the things that your development should provide ways to improve.

169

Epitaph Online

http://drakkos.co.uk

Catering for Explorers


Explorers are the cartographers and researchers. They are motivated by the chance to learn obscure information about their game environment - you are almost guaranteed to know of a few of these off of the top of your head. Explorers may be methodical and highly analytical, or they may be taken to satisfying spontaneous curiosity. Their basic features remain the same. Explorers like to categorise and demystify the black box systems that work in the background of Epitaph. They are responsible for teasing out the hidden information that becomes apparent only through careful experimentation. They are the ones who will be working out which weapons or armours are best in which situations, and what relationship exists between complementary internal systems. They are also responsible for the creation of the many superbly detailed maps that the rest of us have available. In short, they are the scientists of a gaming environment. As can be imagined, explorers like to know things that are not easily known. They like to be able to bridge the gap between action and outcome, and detail the larger context of how things fit together. Unfortunately, such things - assuming they are not changed often - are fleeting achievements. Once mapped, an area remains mapped. Once known, a weapon's damage profile remains known. Changing things around reintroduces mystery, but greatly reduces the satisfaction explorers get from making the effort in the first place, and has a knock-on effect with regards to creating opportunities for players to make informed decisions - if the goal-posts keep changing, where do you kick the ball? Although secrets do not remain secret for long, we can still accommodate explorers in our developments. Large areas appeal to the compulsive mappers, but mapping is a One Time Thing. Secret areas and hidden interaction options are more effective - while mapping is a task of methodically exploring an area, finding secrets is a more intellectual pursuit involving delving into the provided clues and untangling them in such a way that a worthwhile result is obtained. Hidden treasures like rumours, items that are scavenged only be careful observation of the environment and other such flourishes tend to appeal to those of the explorer persuasion. Alternatively, a development that includes a mysterious but ultimately knowable system can provide a great deal of lasting interest. An interesting crime system or gossip system can provide an incentive for explorers to extract as much information as they can from experimentation. However, such systems must come with appropriate rewards - it's not simply knowing something that inspires an explorer, it's knowing something worth knowing. Defining something as being worth knowing is a tricky task. Ideally it will have some tangible benefit in the game - knowing the information makes your own gaming experience more pleasant. However, there's only so often you can provide such a thing, and certainly within a local development the cost of learning a system is usually far greater than the reward that comes from obtaining a little bit 170

Epitaph Online

http://drakkos.co.uk

of situational knowledge. One way around this is to provide information that helps explorers to find other pieces of information. Hinting at quests as part of the information teased out of a system can be a reward in itself, especially if knowledge of the quest is otherwise unavailable. Of course, the existence of quest sites makes quest information very difficult to obscure for long, but the design of the quest itself can help there. We'll talk more on that later. One mechanism for drawing an explorer to your area is in the provision of tools to help measure other things, which is always surprisingly popular. I wrote a thaumometer on Discworld for a player once, purely because he asked for one and I didn't see any reason why not. That turned out to be such a useful tool for mapping the impact of background magic on spell-casting that the explorers spent many happy weeks documenting and experimenting. An hour or so of idle development turned into a huge amount of enjoyment for the game's explorers, purely because it hit the sweet spot of what they wanted.

Catering for Socialisers


Socialisers are a difficult group to explicitly cater for, because so much of their enjoyment derives from interaction with other players. While you can't provide all that much, you can incorporate some elements to enrich interaction opportunities. One method for this is to introduce multi-player activities such as games or quests that require cooperation. Porting text versions of popular board or card games can be an excellent way to get people to socialise board games, poker, and the various multiplayer games available to players are all perfect examples of catering for socialisers. Roleplaying is one particular expression of socialiser tendencies, and providing tools to support that is an important part of keeping your socialisers happy. Providing options for people to tailor their gaming experience in such a way that the changes are cosmetic rather than functional, such as with custom clothing, is a draw for many players. Failing that, creating a shop with new and unique items or roleplaying aids can be a boon to those seeking to increase their sense of personal immersion. For socialisers, it's often more a case of 'being' than 'doing'. More than this though, what you are looking to provide is things for people to actually interact about. Examples of this are the clan system, the interdependencies of the crafting system, and even the simple fact that someone can help you up when you're lying prone. While working within these systems provide real game world benefits, they are primarily interaction aids. They don't make it possible to do anything that couldn't already be done - instead, they facilitate an interaction in such a way that the infrastructure deals with the busywork leaving people free to deal with the people part of the system. There is a danger in this though that by simplifying the busywork you will actually 171

Epitaph Online

http://drakkos.co.uk

remove a lot of the satisfactions socialisers get from actually interacting. Getting people to work together towards a common goal is one of the motivating factors socialisers have for socialising, whether that goal be an interesting conversation, or a putting together a successful clan. When too much of that interaction is automated, then the challenge is gone and so too is the incentive to socialise.

Catering for Imposers


Imposers, like socialisers, play the game to interact with other people. However, that interaction is primarily conflict rather than cooperation. What many imposers look for out of a development are the same things that achievers seek - more personal power within the game. However, the motivation is different imposers seek power to remain competitive against other imposers. High XP yield is a significant draw for imposers - whether this be in the form of NPCs or low complexity quests. Skill opportunities, especially for Killer Viable skills, are also a draw. However, one thing that can appeal specifically to imposers is a framework for interesting player versus player conflict, or for ways to widen the pool of individuals who can be part of the fun. Instances where the environment is designed to be a challenge for player-killers offers one coded mechanism for this, as does our opt-in system of PK anyone can PK for a while if it seems like fun. Within a lone development, it is harder to specifically cater to killers. As with socialisers, the primary lever by which we can manipulate their game experience is the set of Other People. Sadly, no matter how much we wish otherwise, Other People are out-with our control.

Conclusion
This has been a fairly brief discussion of the kind of game elements that can support drawing the right kind of people to the right kinds of developments. Bartle's work is far more detailed than I have outlined here, but it is more difficult to relate to our specific gaming environment since certain assumptions are made throughout (for example, that imposers can prey on non-imposers, which isn't the case for us). Knowing our audience is the first step in knowing of what our development should consist, and we at least now know what direction our planning should be heading. With that knowledge, we can move on to the next step of deciding what specific elements our development is going to have!

172

Epitaph Online

http://drakkos.co.uk

Urban Planning
Introduction
Our last two chapters have discussed the motivation for an area, and the kind of people who are likely to be interested in visiting it. In this chapter we are going to layout out the specifics of what Zombieville is going to be in a way that lets us know, long before we lay down a single line of code, how everything relates to everything else. It's important to note at this point that having a plan is not the same thing as having no choice when it comes time to develop your plan is contingent on real world constraints such as the time you have available, your coding confidence, and the overall requirements of your domain. Plans must be flexible, but you can get an awful lot of benefit out of thinking through exactly what it is you are hoping to achieve.

An Interesting Area
What makes an interesting area? There are many answers to this, but that one that has always resonated most with me is a definition given by an old playtester I knew over at Discworld MUD, a fellow by the name of Griffin@Discworld. It was a very useful benchmark and one that I used throughout the planning of the city of Genua on that MUD. His full posting is available in the appendix of this, and although hugely out of date (the Discworld cities of AM and KLK have been remodeled since he first posted it) in essence it breaks down into three measures: Count the number of rooms in a street or town, discarding inside rooms. Count the number of rooms in that street or town that have a special feature. Divide the number of features by the total number of rooms to get your feature density.

This is an inherently ambiguous metric, because some of these things are difficult to count properly (to which street does a crossroads belong, for example), and features are hard to define and difficult to quantify is a room with a hugely complex gaming system as interesting as a room with commonly available scavengeables, for example? It also ignores things like the quality of the writing, the richness of the immersion, and the overall thematic correctness of the development. At a basic level, features are the things that you go to an area for they could be quests, shops, interesting NPCs, or anything else that would serve as a draw for players. While people may come for interesting writing or mapping, they'll only do that once.

173

Epitaph Online

http://drakkos.co.uk

When putting together your area, it's an excellent idea to keep the feature density in mind, and aim for something reasonably high. If your development has anything lower than a feature density of 0.5, then you should consider either reducing its size or increasing the number of features. Large, empty areas are of dubious benefit to the MUD in general, except when the sheer size of an area is part of its theme mazes, for example. The measure is imperfect, but it has a fairly close match with how interesting' an area feels the higher the density, the more interesting an area it is likely to be. The more sustainable the feature density, the longer that area is going to hold its interest factor.

The Refined Feature Density Model


For the purposes of this document, we are going to look at a refined model for calculating feature density. The refined model is identical in structure to that of Griffin's, but has some modifications to make it a better guide for creator development, although this comes at a cost of increased subjectivity and increased time to calculate. Such are the trade-offs we make in life. We are acknowledging a difference of feature values to different groups of players, so it is illogical for us to use a single measure as a mark of interest to all players. Instead, we will use weighted measurements in a table to work out roughly how appealing our area will be to particular demographics. In the Refined Model, we do not mark a feature as a single point a hugely entertaining quest isn't the same thing as a shop selling a random collection of useless items, although in the Traditional Model this would be the case. This is where our increased subjectivity comes into play we assign each feature a value from one to ten indicating how entertaining we believe the feature to be. We're not always going to be right, but as budding game developers we should be able to make that kind of rough calculation of interest it's part of how we decide what to code. An average feature, such as a shop with unique stock, should be a five on the scale. A five is a take it or leave it' feature. Anything less than a five is something that will fail to draw people to an area. Less than a three is likely to actively put people off. Five is meh, I'll take a look', whereas less is moving towards Oh, that's really of no interest to me'. Additionally, we rate a feature as to how interesting it is likely to be for our particular player groups killers will not find quests as appealing as achievers, who are likewise going to find shops less interesting than socialisers. A true metric is going to reflect the difference of taste. As a further refinement of the Traditional Model, I would also recommend separating out sustainable features from those that are more temporary a quest for example is a draw for a player until it is completed, and then irrelevant afterwards, whereas a shop holds its feature value over the long term. Features in the Refined System are broader too they include not only things to do, but things that happen. They include crime handlers and other immersion 174

Epitaph Online

http://drakkos.co.uk

possibilities. Essentially you list everything about your development that you think is likely to be noticed by players, and assign that a value between one and ten for each category of player. As a worked example of this, I am going to use our safe zone the area between the chain fences that defines the first area of Epitaph. It's quite a hefty area, so rather than list every single thing individually, we'll focus on groupings. We have various features the Winchester itself, of course, which comes complete with a faction. We have a church which, while not especially interesting in itself does have a reasonable number of things to kill for newbies. We've got a playpark that contains some practice objects, and many flats lining the road each with plenty to scavenge and find. Within the Winchester, we have two quests (simple as they may be), a faction shop, and various other things. We don't need to be exhaustive here, we just need to be prepared to make guesstimates as to how interesting different groups will find the area. To start with, let's just rate everything as a five on a chart: Feature The Winchester The Church The Gym The Playpark The Houses Explorers 5 5 5 5 5 Achievers 5 5 5 5 5 Socialisers 5 5 5 5 5 Imposers 5 5 5 5 5

This in itself is a simplified worked example, because we're ignoring core code shared between all rooms in the game, such as the scavenging system and the rumour mill, both of which are germane to this area it's difficult though to separate them out from the larger context of the city itself. Additionally, we're not paying attention to the sustainability of these features a more complex analysis including sustainability is left as an exercise for the reader. The Winchester is pretty heavily packed it has a faction, which offers missions (XP and reputation, of worth to achievers and imposers), a faction trainer (for advancing skills achievers and imposers again). It has two quests (Explorers and Achievers, and since they are simple quests they are of value to imposers also). It contains a faction shop (socialisers), and a couple of secrets that aren't immediately apparent (explorers). It's also a major point of social interaction, since it represents the 'default' area of the MUD as such, it is high traffic and thus of value to socialisers. In short, it appeals across the board. So let's bump it up in all categories: Feature The Winchester Explorers 8 Achievers Socialisers 8 8 Imposers 8

We won't go any higher than eight here, because that's already pretty high and it's not like it is perfect or couldn't be improved with reasonable effort. The church contains some simple NPCs on which newbies can cut their teeth, as well as being the location of a couple of the missions handed out by the faction. 175

Epitaph Online

http://drakkos.co.uk

Let's give it a little bump for achievers and explorers: Feature The Church Explorers 6 Achievers Socialisers 6 5 Imposers 5

The gym contains a few scavengeables, rumours that can be uncovered, and practice objects the latter are free skill increases, and thus of value especially to achievers and imposers. Feature The Gym Explorers 6 Achievers Socialisers 7 5 Imposers 7

The playpark also offers free skill increases, but with little else other than the atmosphere it tries to create. Mark it up for achievers and imposers: Feature The Playpark Explorers 5 Achievers Socialisers 7 5 Imposers 7

The houses that line the street are chock full of valuables, raw resources and such but beyond the actual value of these resources in terms of how they advance and sustain individuals, they have limited interest to anyone but explorers and socialisers. Socialisers will appreciate the fact that there are many options for character customization available by scavenging clothes, and explorers will appreciate the fact that the scavenge profiles represent a mechanism by which information about the searching system can be elicited. Achievers and imposers may like the relatively easy access to food and water, which is a staple for their other pursuits. Feature The Houses Explorers 7 Achievers Socialisers 6 7 Imposers 7

Our next step then is to collate all of this and calculate the feature density of the safe zone for each of these groups. We won't calculate this against street rooms, because we have the situation complicated by, for example, the sheer number of houses: Feature The Winchester The Church The Gym The Playpark The Houses Average Explorers 8 6 6 5 7 6.4 Achievers 8 6 7 7 6 6.8 Socialisers 8 5 5 5 7 6 Imposers 8 5 7 7 7 6.8

Our area then has acceptable feature densities for all four main categories of players that's important for the first area that players end up discovering. The actual feature density of the region is much higher because here we are counting 'houses' as a single feature when they each count as a feature on their own (albeit 176

Epitaph Online

http://drakkos.co.uk

one that is not especially varied). It's not my intention here though to fully calculate the feature density of the area, just to show you a worked example of how you can use it to assess the profile of your work. Reasonable people can disagree on the values I have assigned to things here they're all a subjective judgement call and I make no claims that my numbers are necessarily more accurate than anyone else's. It's not the answer that matters, it's the process that you go through because that illuminates a lot of your thinking.

Feature Planning
There are two ways to begin the planning one is to concentrate on the features first and then work out the area to fit around them, or to plan out the area and work out the features to fill it. We're going to go with the features first' plan, because it helps us decide in advance how big our area can realistically be to keep a reasonable density. To recap on our design goals, we are catering to two sets of players explorers and socialisers. That means that we have several choices of the flavour' of zone we are going to develop: Questing zone Immersion Zone Exploration Zone

Let's plan out a first rough feature-set that is likely to appeal to these particular groups. No details, just a vague idea of what the scope of our development will be. One obvious quest Two secret quests Three hidden areas An abandoned shop containing unique clothes A library that with careful research will yield hints about the quests and some info about other players.

Where do we get this ideas from? Just by thinking about what fits into the theme of the area we're developing. We're not actually building Zombieville to an in-game zone, which means that we don't actually have the benefit of thematic constraints and inspirations. We'll talk about the importance of the theme in the next chapter, because it will help us outline what form our village will actually take. We don't need to know what we're actually going to do here, we're just speccing out what we'll need to do in order to provide a draw to the right kind of people. Even without knowing the specifics, we can start putting together our feature density chart. A quest is more interesting, generally, than any average feature so it starts off a bit higher. It also appeals across the board:

177

Epitaph Online

http://drakkos.co.uk

Feature Obvious quest

Explorers 7

Achievers 7

Socialisers 7

Killers 7

A secret quest appeals more to explorers and achievers than anyone else, but the amount it appeals to an achiever will be based on how much of a yield the quest will have. Killers are looking to maximize their own skills, and anything that requires intricacy is likely to be less profitable on that score than other available opportunities: Feature Secret Quest 1 Secret Quest 2 Explorers 9 9 Achievers 8 8 Socialisers 7 7 Killers 5 5

Hidden areas appeal to explorers, and do not especially appeal to others. Assuming they have no other draw than to be hidden, I'd rate them as follows: Feature Hidden Area 1 Hidden Area 2 Hidden Area 3 Explorers 7 7 7 Achievers 3 3 3 Socialisers 3 3 3 Killers 3 3 3

The shop will appeal to socialisers over any other group: Feature Clothes Shop Explorers 5 Achievers 3 Socialisers 7 Killers 3

And the library will appeal to explorers who want to know about secrets, and socialisers who want to know about people: Feature Library Explorers 8 Achievers 3 Socialisers 8 Killers 3

This is without any real planning we can adjust the values up and down as we get a firmer idea for what course the development is taking. Now, the only two groups we're actually interested in are the explorers and the socialisers. Summing up the feature densities gives us the following: Feature Obvious Quest Secret Quest 1 Secret Quest 2 Hidden Area 1 Hidden Area 2 Hidden Area 3 Clothes Shop Library Total Explorers 7 9 9 7 7 7 5 8 59 Socialisers 7 7 7 3 3 3 7 8 45 178

Epitaph Online

http://drakkos.co.uk

This gives us a measure for what size our area is going to have to be in order to appeal to our chosen demographics anything more than nine rooms is going to drop the Feature Density below five for socialisers, and keeping the density above five is a generally good rule to which we should adhere. Thus, we're going to give ourselves a budget of nine corridor rooms to link all of these up.

Conclusion
The refined feature density model proposed in this chapter is experimental, and at best only a rough measure for guiding your development. It is also not enough to guarantee people will spend time in your area especially if you are, like we are here, catering to demographics that are not especially well represented amongst the Epitaph playerbase. All the refined feature density model allows for is a structured framework for thinking about what your area will need to provide. Use it if you think it'll be helpful, ignore it if you don't. A good game keeps your players happy, and spending this time at the start of the development before you start writing any code helps ensure that your plans are aligned with what is likely to appeal to the right kinds of players. Of course, we are also restricted (and supported) by the thematic constraints of the Epitaph generally, and in the next chapter we'll look at how thematic awareness can inform our developments.

179

Epitaph Online

http://drakkos.co.uk

Thematic Considerations
Introduction
Epitaph is not based on any existing intellectual property, although it is of course hugely influenced by many of the significant works in the post-apocalyptic genre. The lack of existing canon that we need to service gives us tremendous freedom when deciding how our game is to look and feel, but it does complicate things when our own conception of the game world is evolving and incomplete. When there is a blank canvas for a game, then who is to say what is appropriate and what is not? Luckily, we are not entirely in that situation because the apocalypse is well explored literary territory, and we have a good conception of our own real world and can extrapolate a potential game world based on simple starting principles. We don't need to have the entire story when we sit down to develop in fact, it's good that we don't because we can let our game canon be driven by our needs, rather than our game being developed around some inflexible plot.

Starting Principles
So, what are these starting principles upon which our game is based? Well, how about these to start with: The world is set in the near future, although it is not specified when. The game is set an indeterminate period after the zombie infection first hit the timeframe is never provided, but it was long enough for those remaining survivors to have looted most of the city clean. The game is set in an approximation of our own world We are seeking to build a game of survival horror in which zombies are a major feature. We are looking to explore dark territory, driven by the social context in which survivors survive. Our world is set in a non-mystical universe, and the zombie infection is not magical in origin.

These starting principles have a huge impact on the world that we develop it opens us up to explore the darker recesses of human civilization, and use the apocalypse as a mechanism to discuss larger issues of the decay of modern preinfection society. Because it's a close relative of our world, albeit a world in which zombies are possible, we understand the nature of the world physics, the relationship of people to spaces, and the 'feel' of the cities in which people will be adventuring. People will be adventuring in cities that look much like real world 180

Epitaph Online

http://drakkos.co.uk

cities, although the high-street brands that people encounter will be satirized rather than used directly by name. The theme of survival horror then drives a lot of the rest of our design there is a difference between a survival horror game, and an action game that happens to involve zombies. Survival horror implies that survival is an end in itself ammunition will be scarce, food will be scarce, and it should be actually dangerous for people to project their presence outside of the safe zone. The fact that we have defined the nature of the infection to be non-mystical also influences the theme greatly there's no magic in our world (as there might be in a world that was more Cthulhu themed). There's no religious intervention (as might be the case if it was an 'end-times' theme). There's no psychic power (as there might be if we were based on Stephen King's 'Cell'). There is nothing to say that we couldn't have those things in the future if it seemed appropriate it's our theme, after all. But failing changes to the basic principles upon which we all operate, those are restrictions under which we labour. The theme constrains us, but it also channels us there is a reason we don't have dinosaurs wandering around, or Jedi coming by to give the zombies a good beating Epitaph's world is a near neighbor of our world, and in our world dinosaurs are long extinct and Jedi do not exist. To be a coherent game world, everyone needs to understand the importance of extrapolating from these fixed starting points.

The Opportunities
The fact that we have the entire real world to draw on means that we are never at a shortage for things we can include the real world is full of great things we can use as inspiration. Fantasy MUDs don't get to have chainsaws, or pneumatic hammers that can be used as weapons. We can have stuff like remote control cars, flashlights, video cameras, all of the rich taphestry of modern life. And modern life being full of dark, depressing things we also have a huge amount to draw on for when we want to discuss the duality of human nature nobility and savagery in equal measures. Many of the areas I have written for Dunglen have been 'influenced' by locations in my own life that's easy for me, because Dunglen is set in Scotland and that is where I have lived my whole life. There are bits of many locations I know well represented in the city, as well as other locations drawn from across the country. None of them are direct copies, but they served as a useful starting point for visualization. Fantasy worlds, while they offer the opportunity to be truly fantastic, are often difficult to write because they are so unfamiliar. None of us has ever seen a floating city while we view it from the back of a dragon. All of us have seen a Starbucks. While it's not easy to be poetic about your average coffee chain outlet, it is possible to describe it while also providing social commentary on the nature of such outlets their self-concious imagery and colour schemes and the social message they attempt to project while selling hot drinks at giant, unjustifiable 181

Epitaph Online

http://drakkos.co.uk

markups. We have a templating system on Epitaph whereby we can place down copies of certain buildings the fact that we do that is in itself a commentary on the increasing homogenization of the high-street and the power of branding. When deciding on a location or a feature to be included in the game, think about what your real life city provides, and how it could become interesting in the context of post-apocalyptic city. Perhaps it is interesting because of the kind of things that may be found there. Perhaps it's interesting because you have some neat quests in mind. Perhaps it's the obvious location for placing a faction. All of these are excellent reasons to consider a location for inclusion. If you need a system that we don't have in order to justify it, don't let that stop you we have a pretty rapid pace of development that lets us rapidly introduce new features. Once you've got an idea for what your location is going to be, consider how you make it your own. Copying an existing location isn't going to be especially satisfying, but it can be a useful starting point. Move things around, merge the interior with the interior of a similar building you know make it something specifically Epitaph. You'll find that the real world has a lot to offer for inspiration when building a modern, post-apocalyptic world like ours.

Profanity and Racism


Whilst being tremendously foul-mouthed myself (I'm Scottish, we use swearing as a kind of punctuation), I am not a fan of unrestrained profanity in the game. I think it looks cheap on the whole, and worse than that it almost always looks artificial. There's a fine art to good swearing, and the requirements of a game mean that it's difficult to get that kind of natural poetry worked into game dialogue. However, I also appreciate that given our setting, swearing is going to occasionally be a neccessary part of game dialogue. When the word 'fuck' is genuinely called for, it's usually the only word that will do. I appreciate there are a lot of people who believe profanity is symptomatic of a limited vocabulary, but I am not one of them - I think the sheer emotional and cathartic qualities of a curseword is often difficult, if not impossible, to express just by being more eloquent. Within the world of Epitaph, swearing is permitted - but only when you are putting words into the mouths of NPCs. Thus, it is okay in dialogue, and okay in graffiti. It's not okay in long descriptions or add_items. You can have:
The rough sailor says: Fuck me, zombies!

But you can't have in a long description:


This is a pretty big knife. no mistake. You could cut up some fucking bitches with this and

This might seem like a strange distinction to make, but again it is about theme. 182

Epitaph Online

http://drakkos.co.uk

We as the builders of a world are the dispassionate creators of a reality - we're not affected by it, and thus we can afford to be more articulate in how we attempt to present that reality. Our NPCs, and by extension their interactions with the world, are being affected by our reality on a day to day basis. When attacked by a zombie, no-one is going to say 'Oh my, this is a sad state of affairs and no mistake!'. They are going to say 'Argh, I am being eaten by a fucking zombie!' or something similar. Similarly with racism - we live in a world where racism is rampant, and while we can gloss over that by simply never mentioning it in the game context, that doesn't ring true. Violent racist imagery, graphic racist epithets and characterisations of racial minorities are omnipresent in day to day life. I'd hope it goes without saying, but genuine racism has no place on Epitaph. On the other hand, if an NPC should be racist, then there is nothing in place to stop you. Remember though - you are putting words in the mouths of NPCs. The text that makes up the dispassionate lens through which our players view the world must never be either profane or racist. I appreciate that it is much easier in both cases to simply say 'Well, in this game world we don't have either of those', but it always strikes me as ringing hollow. Those who have such delicate sensibilities that they cannot bear to see real world ugliness reflected in a game with a theme such as ours should perhaps look elsewhere for their entertainment. That doesn't mean that you should overuse the devices - just be aware that they are available when it's neccessary to enhance characterisation or thematic consistency. The trucker who says 'Dang it all to heck' forcefully breaks my immersion - people just don't talk like that, on the whole. The television show that promotes a diverse panel of multiracial integration always strikes me as being horribly, cynically manipulative. We all know that's not the world we live in - the world of Epitaph should be a reflection of the real world as far as it can.

Humour
We're obviously not a comedy game, but that doesn't mean we need to be dreadfully po-faced all of the time. One of the great things about the human species is that we can laugh even in the face of the most horrendous acts. Humour then is not something you should shy away from, but it should be thematic humour. Black humour is a suitable humour category for us, and it covers those subjects that are considered 'taboo' or off-limits for regular, every day comedy. Death, sexual violence, racism, and war are all common subjects for black humour. The idea is not to diminish the subject matter, but instead to create an atmosphere whereby the audience (our players) are simultaneously made to feel amused and also a little discomfited. In a world of unremitting darkness, people will find the humour in practically anything, even if only as a mechanism for releasing some of the internal pressure on their minds. Related to this, but subtly different, is gallows humour - it differs primarily in the 183

Epitaph Online

http://drakkos.co.uk

fact that the source of the humour is the same as the target of the unpleasantness. Quips by those about to be executed are a common example of gallows humour - a man about to be shot by a firing squad, upon being offered a cigarette, says 'No thanks, I'm trying to quit'. Benjamin Franklin, when saying 'We must all hang together, or else we will be hanged separately', was employing gallows humour in the face of an almost certainly devastating British counteroffensive upon the signing of the declaration of independence. In the Discworld novels, Vimes muses 'We who think we are about to die, will laugh at anything'. In House of Leaves, there is a beautiful expression of gallows humour: Zampan, I've come to recognize now, was a very funny man. But his humor was that wry, desiccated kind soldiers whisper, all their jokes subsurface, their laughter amounting to little more than a tic in the corner of the mouth, told as they wait together in their outpost, slowly realizing that help's not going to reach them in time and come nightfall, no matter what they've done or what they try to say, slaughter will overrun them all. As is often said - you have to laugh, or else you'll cry. Take advantage of that basic trait of humanity if you feel that you need to lighten the atmosphere a little bit.

Player Input
It's often difficult to think up good ideas for what you'd like to include. It's all very well to say I'm going to write a quest', but you're going to need a formal plan for that quest before too long. Sometimes the books don't give inspiration and you just can't think what to do. In such cases, the playerbase can be a useful source of ideas. It's sad but true the vast majority of ideas submitted by the playerbase are unusable. They are either so exhaustively detailed that no creator wants to code them (half the fun is to plan the plan, as Mrs Lovett has said), so unbalanced that they would break the game entirely, or so vague as to be completely worthless. However, every now and again, there is an idea that is just perfect. Someone will make an idea report such as 'Hey, we should be able to set fire to people with these moltov cocktails', and a creator will see it and think 'Yes, that does sound cool'. Sometimes that's all it takes the idea gets to the right person who needs it at the right time, and it flicks the right switches. We have several locations where player ideas get recorded. Your first port of call should be to our ideas interface, which is available both on the webpages and in the game through the 'idea' command. This is a good starting place because it is partially community managed players vote for ideas they like, and vote down ideas they don't. Ideas that are highly rated are not necessarily useable for various reasons, but they at least have popular support and that is worth something. Sometimes, when we find an idea that we like and want to show our support for, 184

Epitaph Online

http://drakkos.co.uk

we put it on our 'rainy day file'. You'll find that on the wiki it's essentially a list of ideas people have had, and that a fellow creator has said 'Yes please', but didn't have the time (or perhaps the expertise) to code it. Usually these will be drawn from the ideas system, but they are especially worthwhile because at least one creator has seen them and decided that it was worth the time to record. Playtesters are usually a source of ideas that are more considered than that of normal players, and reading the Playtesters wiki and the ptforum board can be a source of inspiration. The player boards too, although a more mixed bag, are worth keeping an eye on to see what people are enjoying or complaining about. We encourage player feedback for the occasional diamond in the rough but you have to work to find those ideas. However, even reading bad reports can trigger off that little connection in your mind that leads you down a path of inspiration. Whenever you are lost for ideas, just spend time reading the ideas of other people. Make a note of ideas that appeal to you for your own rainy day file. Not every day is going to be a day in which you come up with great ideas, and other days will be full of more ideas than you can hope to code. Keep track of them as you go along so that you have a fallback' list.

Back to Zombieville
So, now that we've spoken a little about thematic awareness, let's return to our plan for Zombieville. We have the luxury here of deciding on a theme of our own because we are not limited by a domain plan. We could set it in England, or America, or any number of other places. Let's not be adventurous though, let's set it in an area in which a lot of the theme is already defined. Certainly for our first development, having constraints will aid us produce an area that fits into the rest of the MUD. We're going to set this village in Scotland like most of the United Kingdom, Scotland has been ravaged by the infection and is full of the undead. The remoteness of the country and the relatively low population density have had an impact, but they have not protected its major population centres from the ravages of the plague. Scotland is dotted with little towns and villages, and as such we can have a development of practically any size from a few scattered farmhouses up to relatively large rural locations consisting of a dozen or so streets and hundreds of houses. We're not going to be as adventurous as all of that our village is going to be quite small (nine rooms, as we decided in the last chapter), but knowing where the village is set gives us some clues as to where our thoughts should coalesce. The Scottish landscape is hilly, and the transport infrastructure is very shaky in places while some villages are well served by motorways or trains, there are plenty for whom car travel is the only reliable way to get to 'civilization'. That has an impact on the kind of village we are likely to develop is it remote? Is it quite central? Is it in the highlands? Or the lowlands? Reading up on Scotland and looking up webpages of some of Scotland's villages 185

Epitaph Online

http://drakkos.co.uk

can be a worthwhile exercise for those who have never been here, but it doesn't really matter if it's realistic. It just has to feel realistic. Because we have such a limited budget of rooms, it's probably a good idea to go for something remote we can't afford a train station in our budget, and nine rooms can perhaps accommodate a couple of small streets, or perhaps one street with a village square. That's not a lot to work with we could increase our budget by planning for more features, but that's not a great idea (we'll talk about feature creep later in this section of our book). It's best if we work to the budget we have. So, we have a small, remote village. Let's think about what such a village might have. A small local shop is one possibility. It would almost certainly have a pub, which in Scotland as in elsewhere is very much the heart of a small community. Being in the countryside, it's also likely to have a couple of farmhouses dotted around. It would have little cottages too, but that's going to be true of anywhere. Let's relate this basic idea to our feature list, and see if we can get it to mesh up: Feature Obvious Quest Secret Quest 1 Secret Quest 2 Hidden Area 1 Hidden Area 2 Hidden Area 3 Clothes Shop Library Total Explorers 7 9 9 7 7 7 5 8 59 Socialisers 7 7 7 3 3 3 7 8 45

While a clothes shop is not the most immediately obvious option for a small village, it's not impossible after all crafts shops are a common feature in small communities the world over. An abandoned crafts shop, full of tasteful tartan knitwear, could be just the thing to fill that role in our feature plan. A library doesn't seem like something that would be a likely feature in a small Scottish village, unless we widened the scope a little what if if it was a library in a village school? A village school is certainly possible, and would be likely to have a small library associated with it. Perhaps it has been barricaded up against the undead, but eventually had to succumb as all fortifications must when the besieged are not being resupplied. So, that is our area a small village, complete with village school and a tartan knitwear shop, perhaps one that contains useful crafting materials for players. It's by no means the only way we could go with this development. We could just as easily have decided on a small castle, or a salmon fishery, or pretty much anything. This is just one way we could choose to go, and since it supports my goals of providing an effective teaching scenario for a later section of this handbook, it's the one we're going for.

186

Epitaph Online

http://drakkos.co.uk

Conclusion
When working within a world with a well defined theme, you have to know what that theme is. More importantly, you have to know what you can do and what you cannot. However, when the theme is being defined by the people building the game, it's more important that the development is consistent with basic first principles because often the game canon simply won't exist until it has sufficient need to be written. Those constraints are not problems you need to work around, they are the very meat and potatoes of making your area seem like something that fits into both the books and the larger game world of the MUD. The best investment you can make in yourself as a Epitaph creator is to read books that are appropriate to the theme zombie books, apocalypse books, even books on simple human nature. It may be a bit much to ask you to love them... there are actually some Epitaph creators who are not especially fans of zombies in particular... but a love of the Epitaph setting will help you make the jump from abstract plans to more concrete, thematically consistent locations and that's very exciting.

187

Epitaph Online

http://drakkos.co.uk

The Best Laid Schemes


Introduction
Each of our chapters so far has taken us one step closer to defining exactly what we're going to have in our area. At this point, we have the demographics to which we are going to appeal, the features that we are going to provide to ensure that the right kind of players are supported, and a theme for our development that is both original and consistent with the Epitaph setting. In this chapter we're going to finalise these plans and decide on exactly what form our development will take. We'll decide what each of the quests are going to be, and what hidden areas will be present. We'll also sketch out the map of the region according to the constraints we have set ourselves (nine corridor rooms).

Making the Map


A good first step to help bridge the gap between vague plans and specific plans is to develop a map of how things will link to other things. In Introductory LPC, we looked at putting a skeleton area together based on such a map, and that's always a worthwhile thing to do. Trying to put together an interesting layouts of areas is part and parcel of a development - street rooms are not likely to be 'interesting' in the technical sense we have previously defined, but they can very easily be boring. We have nine rooms, which isn't a lot, but it's enough for a small village square and five further connecting rooms. How we choose to link those up is entirely our choice. We could be boring and make it all along a single line.
5 - 6 | X | 1 - 2 - 3 - 4 - 7 - 8 - 9

Yawn. Although having to change direction when travelling is not an interesting decision, it's a decision nonetheless. All you're doing to get from the end of this village to the other end is travelling in one direction. A drinking bird toy could handle that for you. We could make the layout a little more inspiring by mixing it up. Almost every village square is a 'north, south, east, and west' affair. You could easily rotate that by 45 degrees to make one that is all diagonals:

188

Epitaph Online

http://drakkos.co.uk

6 / \ 5 + 7 \ / 4

It's a very small change, but one that instantly changes the topology of the village from the traditional 'Ho hum, another village square' to something a little more interesting. Likewise with the path, there's no reason why it has to go in a straight line. We can make it meander a bit. Also, we could have a branch off of the main path leading elsewhere rather than having a second path leading off of the village square. All of these are more interesting choices than having a simple, linear progression.
6 / \ 5 + 7 \ / 4 / 3 | 2 / \ 1 8 \ 9

We have exactly the same number of rooms - all that has changed is that we have laid them out a bit more exotically. An interesting layout will lead to easier descriptions - for the first map, it's all going to be variations on the theme of 'The path leads east and west', whereas with our second map we have many more interesting possibilities. We'll be using Map Two as the basis for Zombieville when it comes time to code it.

Incorporating Features
The easiest thing to do to begin with is work out what our secret rooms are going to be. We have a number of choices: We We We We can can can can make a key thematic feature of our area hidden make the library itself visible, but rooms within it secret dot hidden areas around the village have a blend of these.

We need to decide which of these is likely to be a more interesting scenario. If we make the school hidden, we are instantly making it more difficult for people to enjoy the area since a key feature is already missing. It'll be of appeal to 189

Epitaph Online

http://drakkos.co.uk

explorers, but everyone else is going to be disadvantaged. People won't necessarily even know there is a school unless we hint at it elsewhere in the area perhaps in room chats or rumours placed throughout the development. If we make the school visible, but rooms within it secret, then people will find our key feature, and the attention of individuals is always higher when they have come to a location that screams 'This is not mere scenery'. People are likely to investigate an obvious library in a way that they won't investigate a street room. Having the school visible hints that there may be further mysteries within. We can dot hidden rooms around the village, and have the school open throughout. This will have the effect of making the village itself more interesting (but only to explorers), but the school less interesting. People in general are going to be coming to our village to investigate the tempting confines of the school, because its very presence is a distraction from the rest of the village. Our final option is to have a combination - we don't need to put all of our features in a single subarea of the development. We could have some secret rooms in the school, and some elsewhere in the village. Personally, I favour having the school being a trove of hidden secrets since that's where people are going to expect them to be. Partially we want to provide a challenge and a reward for people who pay close attention, but on the other hand we are writing for an audience and our audience is reduced if people don't get to explore our creations. As an extra bonus though, we'll make the way into the school itself a secret, albeit one that is fairly well signposted. The school itself can be more than one room - when we talk about 'corridor rooms' we mean rooms that link features to other features. A feature can be a small zone all of its own, but bear in mind that beyond a certain point you should do a feature density analysis of the subzone itself to ensure you haven't made it boring by virtue of sheer size. Let's have three rooms for our school - an opening hallway, the classroom, and the second floor. Careful exploration of the school will reveal that each of these rooms has a secret to tell. Where is the school going to be, in our development? Well, we've created a branching path off of the main road, and we're going to make it a secret path. There should be a reward for discovering a hidden path, so let's put the school at the end of it. The shop is a simple one to place - it'll go in the village, and it doesn't really matter where. We'll worry about where the quests will go once we have placed the physical features.

190

Epitaph Online

http://drakkos.co.uk

The School
A rural school in a Scottish village doesn't require a lot of explanation, but we do need to spend a bit of time considering how it is going to have been affected by the world situation. I previously suggested that perhaps it had been barricaded, and that seems like a sensible approach. We can construct something of a narrative within the library. Perhaps the infection struck the village suddenly, and the teacher (or teachers) thought it best to make use of the classroom supplies to barricade the children until help arrived except, help never did arrive and the food contained within the school was not plentiful enough to support a long siege. How did the teachers respond then? Did they simply wait it out, rationing out the food to the children and trying to turn a deaf ear to their entreaties and suffering? Maybe when it became obvious no help was coming, they attempted to ease the suffering of their charges before their starvation became too much to bear. Perhaps there was a darker tale upon seeing the situation getting worse rather than better, perhaps the stronger adults turned upon the children. Canabalism is an easy way to get fresh meat, after all and how much of a fight could some kids actually put up? Perhaps the inside of the school is a slaughterhouse of small, butchered corpses. You can go as dark you like, really our theme is mature, and so too can can be our stories. The story will unfold on the basis of the clues you leave in descriptions it's unlikely anyone living will remain, after all. On the other hand, you might encounter an undead headmaster/mistress

Instances
The possibility of a boss encounter within the school raises the possibility of the school being an actual instance, which is something Epitaph supports. For those who have never encountered an instance, it's basically a copy of a set of rooms that is shared by a group of players. Every group gets their own copy of this instance, so while there may be four groups in an instance, they never see each other. There are all sorts of reasons why this is useful the main one is that it allows for quite directed story-telling in a way that we can't do in the world. If we require an NPC to be there to tell a story, we can't guarantee that in the real game world someone might come along and kill her. We can't guarantee that things that get done in the game world get done in the right order by the right people. We can ensure that with instances. They're also good fun for groups, especially if they come with a significant challenge such as an interesting boss NPC. Let's pencil that in as a what we want to do an instanced School complete with a headmistress boss NPC. 191

Epitaph Online

http://drakkos.co.uk

Instances can be as interesting locations as regular, common or garden areas if we want to have an instance with quests, that is entirely within our grasp. On the other hand, we may prefer to have the instance separate and the quests actually lead to the entrance to the instance. We have complete freedom here. Because we need to come down on one side or the other for this (and no side is overwhelmingly better), I am going to say that the school will be a normal area, but solving the quests will give access to an instance containing the headmistress.

The Shop
We mustn't neglect the shop - we could just slap one down and fill it full of clothes that are already available elsewhere, but that barely counts as a feature. Instead, we want this to be a source of things people would actually want. As with anything else we develop, we have to be sure to keep our thematic constraints in mind. What kind of clothes are likely to be sold in a small, rural village? Well, real life experience shows that experts can be found in the most unexpected places, so in terms of the realistic ability of the tailors in question, that's not a limiting factor. What we do need to consider though is clientele. Who would come to this village to buy things? Again, this doesn't have to be hugely limiting - people will travel a long way for a genuine genius, but it's best if such geniuses are rare. Most villages don't have a Leonardo, but they might well have someone who is a dab hand at knitting. We'll leverage the usual scavenging code to support this, but we'll make sure we knock up some unique items to make it worth the while of someone searching. Since it'll be a craft shop, it'll have a pile of tartan knitwear, along with some basic craft components for those who are looking to expand their crafting skills. Perhaps even a schematic or too now that kind of thing is what makes a feature actually interesting to people.

It All Adds Up
Every decision we make about the theme opens up a new opportunity for making it all fit together. So far we have, for our village, the following defining characteristics: A rural school containing a small library An instance made available by completing the quests A tartan knitwear shop containing clothes, crafting supplies, and some schematics An undead headmistress with Awesome Boss Powers.

All of that has been spawned from coming up with a simple list of features and considering how our theme influences those features. Considering what the implications of our design decisions are likely to be also influences further 192

Epitaph Online

http://drakkos.co.uk

development. One thing that you may have noticed from our plan is that our village is tremendously sparse - one shop, and all the other features are concentrated in the school. This isn't necessarily wrong, but it does suggest if we want to get the best out of our development we should direct any other features that occur into the village. As discussed earlier, a village pub, or a church, or a small groceries store would go a long way towards making it seem less of an irrelevance. We can worry about that later though, when the core of our design is in place. We add features whe we need to and when we have a gap in our timetable, because otherwise we never stop adding to our plan.

Conclusion
This chapter is a worked example of turning vague plans into solid plans, and it is by no means the only direction this development could have taken. Interested readers can consider working up their own plans based on the thematic constraints we identified in the last chapter - you may be surprised how different your results can be with only a little imagination.

193

Epitaph Online

http://drakkos.co.uk

Quest Design
Introduction
In this chapter we're going to look at some of the design decisions that go into creating a quest for Epitaph. Unlike games such as World of Warcraft where quests are mass produced and number in the thousands, Epitaph adopts a more handcrafted approach to the quests we put into the game. Quests are usually the most challenging thing that a new creator will attempt to write they are non-trivial to plan, and non-trivial to execute. Nowhere is the need for planning more apparent than in the design of even a fairly simple quest. Before we get to that though, let's talk a little bit about different kinds of quest design philosophies.

Why Quests?
Why do we actually have quests on Epitaph? Partially it's a legacy from the old text adventures that inspired multiuser dungeons in the first place such games were all about progressing through a story, and in order to progress you had to solve puzzles. There were rarely any hack and slash' elements to these adventures, it was all about figuring your way through the game quests. A text adventure with no quests is merely an interactive story. Within these adventure games, each quest that you solved took you a step closer to the conclusion of the cstory, and if you didn't solve the quest your progress through the game was halted. Quests thus had a narrative payoff associated with them they advanced the personal story of the player character. Quests are also about providing alternate models of advancement through the game. Quests give substantial experience rewards, and occasionally also reward money or skill levels. Certainly quests that require only brainpower to solve permit new players to gain skill levels in a way they would otherwise find somewhat difficult. Quests therefore carry with them advancement possibilities. Since our quests are puzzles, and since a player needs to be observant to find them and to solve them (assuming they are not making use of a quest list site), they also have an inherent value in terms of providing an additional challenge for players. In the same way that a crossword or a sudoko puzzle is enjoyable in and of itself, a good quest should give enjoyment by challenging in a new and interesting way. Finally, there is a sense of satisfaction that comes from having completed lots of quests, especially when at least some of those quests can be construed as a mark of honour. The social status of achievers in particular can be enhanced by having 194

Epitaph Online

http://drakkos.co.uk

a large number of quests to their name.

Hand-Crafted Craziness
Epitaph's approach is very much a case of quality over quantity'. The quality of quests will vary from place to place, coder to coder, and indeed in the perceptions of player to player but the fact is that each of our quests are more or less unique. This puts a tremendous amount of pressure on a creator because the off the shelf' quest designs so prevalent in large MMORPGs are usually those that we try to avoid. If we think about most of the standard quests in such games, they tend to break down into variations on a handful of themes: Killing quests Drop quests Escort quests Courier quests

A killing quest is one in which the objective is to kill a certain number of a certain type of NPC kill ten green pigs', for example. A drop quest is one in which you have to collect a certain number of items bring me four left -handed screwdrivers', Sometimes these get combined into quest that involves you collecting items that have a percentage chance of dropping from certain NPCs bring me six wolf teeth'. A feature of many of these hybrid quests is that they are extremely jarring from an immersion perspective. If you are asked to collect ten yeti spleens, and there's only a 30% chance of a spleen actually dropping, you have to wonder how all these other yetis can walk around without one. Escort quests involve you protecting an NPC as it moves from one place to another, usually as it is attacked at several points by multiple attackers. Courier quests involve you taking an item from one person to another. Potentially this has a middle stage of having to first collect the item. Go pick up the ruby nipple from the temple of Zog, and then take it to my friend Stabby Joe over in Deadville'. All of these can be automated in the code-base, meaning that the task of adding a new quest is as simple as deciding on some descriptive text to go with it, a reward, and a target. Such quests can offer a narrative payoff through the descriptive text, are often a source of wealth and experience, and often provide useable items as a reward. What they do not offer though is a sense of inherent enjoyment they are means to an end. Killing ten boars because you have a quest is exactly the same thing as killing ten boars when you don't have a quest the quests themselves just provide a context for rewarding such actions in a consistent way. We do have these quests on Epitaph, but not as quests instead, they are handled as randomly generated faction missions while other games may pass them off as substantial content, we acknowledge that it is possible to build these kind of 195

Epitaph Online

http://drakkos.co.uk

tasks from templates. Each faction has a shared bank of these, while some factions may have entirely unique missions designed for their own particular thematic considerations. Our quests are puzzles our missions are what covers everything else. That's not to say that games employing the quantity over qual ity' mechanism have no interesting or satisfying quests indeed, there are often several of these in even the most ardent hack and slash MMORPG. They are very much the exception though, and stand out precisely because they are so unusual. By making our quests hand-crafted, we have a vast wealth of possibilities in terms of what a quest actually looks like. We are limited only by our imaginations, and that is tremendously exciting from the perspective of a developer. There's more satisfaction in hand-coding an interesting quest than there is in writing up a configuration file to be slotted into a handler along with a thousand virtually identical files. And likewise, there's more satisfaction to be had from the players that enjoy the quest you wrote. People don't talk about identikit quests in the way they talk about hand-coded quests. I have never had a conversation in which someone has said Oh yeah, remember that quest where you had to kill twenty plagued ocelots? Yeah, that was awesome'. Quests that stick in the mind are the ones that go above and beyond the call to provide something unique and interesting as to their structure. The reward for such quests is partially simply completing it.

The Anatomy of a Epitaph Quest


So, we have identified that we are going to have three quests in our library our next step forward is to decide what those quests are actually going to be. We won't do that in this chapter, but we will first talk about the features of quest design that are particular to Epitaph. Knowing what the conventions are makes it easier to guide your thinking. These aren't inviolable rules, but if you want to bend (or break) them you need to get approval from your domain leader. So, in no particular order, the ten commandments of Epitaph Quests! Except, as previously mentioned, they're more guidelines than actual commandments: Quests Do Not Reward Must-Have' Items The rewards that come from quests should be proportionate to the effort that was invested. Very rarely does that effort reach the level that would warrant a musthave item. If an item would take ten hours of advancing faction rep to attain, it's not appropriate that it be a reward for a quest that can be solved in five minutes. In practice, it's impossible to judge that amount of invested effort into a quest, especially since any quest you write will eventually end up recorded on a quest site. Most quests can be resolved down into a set of linear steps (but again, more on this later), and the quest that you thought would probably take three hours to puzzle out may be possible to solve in five minutes with the help of a solution. 196

Epitaph Online

http://drakkos.co.uk

Must have' items throw the balance between risk and reward out of whack, and tend to disproportionately reward those who invest the least amount of effort. As such, as a rule we do not reward must have items. Instead, we reward either flavour items, or items with only moderate usefulness. Quests Do Not Reward Stat Increases Most of the balance of our skill-stat model comes from the fact that we know how many stat points individuals have to invest. If extra points are then made available just as a consequence of completing a quest, then it becomes less important for people to consider where their points will be invested, and also possible to power-game' the system in ways we have not intended. Where we make stat increases available (as we do with some knacks), careful adjustment of the context is important. A stat point is a tremendously valuable artifact, and no amount of invested effort can really justify it as a reward for a quest. Quests Do Not Reward Skill Increases Skill increases should come as part of action it's perfectly okay (and indeed, encouraged) to have quests that involve skill checks. The TM opportunities of a quest are a good way to make it possible to give a few bonus points of a skill to a player. Our taskmaster is what regulates such rewards, and it makes sure that levels awarded are a function of how experienced a player already is. The five points of combat.melee.blunt that you decided to give out in a quest may be worth only 1000 XP to complete newbie. It is worth much more to an older player, especially one with skills higher than the game currently supports in terms of available challenges. Quests do not significantly restrict access to other parts of the game Participation in the game shouldn't usually be based on whether or not you have completed a particular quest. It's okay to make a few rooms accessible only as part of a quest (as we are planning to do for our rural school) but it's less okay to restrict larger areas the larger the area, the less okay it is. When you restrict an area based on a quest, it should be restricting access to 'extra content' rather than core content. The reason for this is pretty simple we don't want quests to be something that people have to do, but rather something people want to do. When it's mandatory, it becomes a chore rather than anything else. It forces people to play the game in the way we have chosen for them, rather than in the way they have chosen for themselves. Quests shouldn't require disproportionately large levels of useless skills

197

Epitaph Online

http://drakkos.co.uk

There's little more frustrating than a quest requiring a hundred levels of other.stupid and that skill being of use nowhere except for that particular quest. This is actually a two-pronged problem one is that some skills are useless (and we need to address that with game features that make use of the skills), the other is that your quest is asking too much from a player. Asking too much in the way of skill levels is a tremendously easy way to make sure that the cost of doing the quest dramatically exceeds the reward. If it's a flavour' skill, then set the absolute limit at something that can be achieved with an investment of a few hundred thousand XP. You don't want to discourage people from doing your quest purely because the net result will be to actually cost them experience. In both cases though, keep in mind the ceiling that factions can teach, and that the cost (in terms of invested time) dramatically increases beyond the faction maximum. Quests do not have lethal consequences Quests are about fun and experimentation, not lethal consequences. Players shouldn't be killed as a result of trying to work out your puzzle unless it's adequately signposted. Having a big button that says If you press this you will die' is an example of something being signposted the fact is people won't necessarily believe it, but you can't help some people. On the other hand, having to choose between three identical-looking chemical beakers, two of which will kill you instantly, is not a good design choice. This is not to say that your quests cannot involve risk that risk just has to be proportional, and it has to be opt-in. Players have to know that what they're doing has potentially deadly consequences. Challenging a high level NPC to a fight is something that has an obviously potentially deadly consequence, and players can opt-out of that fight when they feel it is going too badly. Dropping someone into a pit of fire with no escape because they pressed the wrong unmarked button on a dashboard is unfair and frustrating for the player. Quests should be easy to find, for a given value of easy By this I don't mean that a quest NPC should prod everyone who passes and say Hey, I've got a quest for you'. Warcraft signposts quest NPCs so that you don't look for quests, you get told exactly where they are. The opposite extreme is quests that are obliquely hinted to in an add_item three or four references down. It should be possible for an averagely observant player to get a hint that a quest is somewhere nearby. We have a 'questsense' command that serves to bridge the two extremes those who want to discover quests organically are never prompted of their existence. Those who want a bit of help, or want to see if there actually is a quest associated with some in game object, can use the command for hints.

198

Epitaph Online

http://drakkos.co.uk

This is in your best interests too quests that are too hard to find are solved honestly by the smallest subset of the player-base. The rest of the people who solve it were directed to it by a quest list of some form. It takes a lot of time and effort to code a quest, and you want people to be able to gain enjoyment from that effort. Quests should use obvious syntax, for a given value of obvious It's really hard to predict what commands people are going to use to solve your quest, and even harder for you to make sure that everyone is going to be happy with the syntax. The most frustrating quests on Epitaph are games of guess the syntax' in which the only puzzle is what arcane, unintuitive command will unlock the tasty candy of the reward. We've all done it, it's one of the consequences of working within a code-base that lets you do pretty much whatever you would like. It's difficult to know in advance what is going to be good syntax, but try to make it something obvious. If you want someone to pull a lever, then make the syntax pull a lever', don't make it tug on lever'. Use simple, clear syntax whenever you can. In the Discworld lib, from which we derive, that was complicated by the awkward interaction between real commands and soul commands you don't have that problem here, because all soul commands are prefixed with a /. Use the commands that make the most sense, and be open to criticism when people say that they are oblique. Quests should not involve hard-to-get or rare items Once upon a time on Discworld MUD, one of the hardest items to get in the game was a simple oil-can. It was available for a few pence from a shop on the Plaza of Broken Moons, but only one was available per reboot. If you were the first person to the shop after a reboot you could buy it and sell it for a hugely distorted profit, or use it to complete the simple quest to which it was linked. When the only thing stopping you from completing a quest is getting hold of some hard-to-obtain item the quest becomes a waiting game rather than something you are actively trying to solve. It eventually becomes something you'll try to solve if and when the stars and planets align in such a way as to provide you with the necessary luck to actually obtain the item upon which success is contingent. That's presumably not what your intention was in writing the quest. This is especially problematic if your item is available in another area of the MUD, because it's often the case that areas get moved around, temporarily (or permanently) shut down, or remodeled. On Discworld, people sometimes reference a green gem' this was an item that was available at one point, but stopped being available as the area in which it was found was closed down. A few of them lingered around in private hands, usually in the vaults of players who kept them just in case'. This wouldn't be such a big deal if it wasn't for the fact the gem was required for a quest with stupidly overpowered rewards. Making quest items rare distorts their value, and creates a small economy around their procurement. Obtaining the item becomes a matter for speculators rather 199

Epitaph Online

http://drakkos.co.uk

than questers, and that is hardly ever a good situation. Quest should have logical behaviour and an obvious goal If trying to guess the syntax of something you want to do is frustrating, imagine if you were trying to guess the syntax when you weren't actually sure what it was you were trying to do. All quests should have an obvious goal that leads the player (with thought) to the solution. A quest in which you have to flick a piece of cheese through an open window needs you to explain why this is something the player should try to engineer. You can provide mention of the cheese in the room, and the open window, but there is no logical reason why the presence of both implies the cheese should be launched airborne. On the other hand, a quest in which you attempt to ping a ball of cheese to a terrified, starving mouse in a mouse-hole has an obvious goal there's a starving mouse, and you have some cheese. It won't come out of the mouse hole, so you must provide the cheese to the mouse. Additionally, the behaviour of your quest should make sense not only should you know what it is you are supposed to do, you should be able to rationally plan out how to reach the goal. The canonical example of a quest in which this was not the case is the Babel Fish Puzzle from the Infocom text adventure of Hitchhiker's Guide to the Galaxy. The Babel Fish is a fish that translates any language when it is placed in an individual's ear, and the game has a puzzle whereby the player must engineer such a desirable state of affairs. The solution breaks down as follows: Pressing a button dispenses a Babel Fish, but it shoots out at such speed that it flies across the room and into a hole. The player must put a dressing gown on a hook above the hole. This causes the fish to drop down a drain which must be blocked by a towel. When the fish drops onto the towel, it is cleaned away by a small cleaning robot that instantly darts into the room, cleans, and disappears via a small panel. This panel is thus blocked with a satchel so that when the robot darts in, it cannot escape. Instead, it throws the fish into the air to the attention of a second cleaning robot responsible for the upper half of the room. This second robot is dealt with by placing some junk mail on the satchel so that when it is sent flying into the air the robot is sufficiently busy so as to miss the fish. There is no way a player can plan this out in advance, and to be fair this actually breaks down into several sub-puzzles each with a fairly well defined goal: Stop Stop Stop Stop the the the the fish flying into the hole. fish falling down the drain. robot escaping with the fish. second robot grabbing the fish when it is thrown in the air.

Solving each of these leads to the next in the chain, but the problem is that the last step of the puzzle violates the principle of having an obvious goal, and also having logical behaviour. This is made especially unforgivable in that you can continue with the game without solving this problem, but it makes the game 200

Epitaph Online

http://drakkos.co.uk

impossible to complete and you don't find this out until you are almost at the end of the game. When planning out the stages that people must go through in your own quest, make sure that they progress naturally from one to the other, and that the behaviour to bring about a goal from a starting state is something that can be discovered by more than trial and error.

Conclusion
Quests are going to be the most intricate things you code as a new creator, and also one of the things that are easiest to do wrong. In our next chapter we will continue to talk about the principles of good quest design, because we're not even close to being done with this topic. All we have really done here is discuss the general principles upon which we try to build Epitaph quests... we haven't talked about actually designing them yet.

201

Epitaph Online

http://drakkos.co.uk

Let's talk about Quests, Baby


Introduction
Now that we've spent a little time discussing the anatomy of a Epitaph quest, let's bring it back around to focus on our own development and the specifics of how our own quests are going to work. As usual, there's thinking that needs to be done before we get to that point so far we've only really spoken about constraints. We're planning to have three quests one secret quest to reward explorers, and two more obvious quests that should be easier to locate for everyone. We've also decided that our quests will take place in our rural school. All of that has emerged naturally from our thematic considerations. Now we need to make actual quests emerge!

The Intention of a Quest


First of all, let's consider why we're putting these quests in our development at all. We've spoken about this in general terms in the last chapter, but let's look at in terms of what's specific to our own area. In short, what is it that we want our players to get out of the quest? Do we want them to get: A challenge? A tangible reward? Access to a feature? Nothing beyond the fun of the quest itself?

The design of our quests will vary according to what they are supposed to be generating for the player. Most quests are simple for the fun of it' you find them, you do them, you get some XP and then move on. That's absolutely fine, but in certain circumstances there is much to be said for quests that give something beyond that. That's the first thing we need to consider, because the quest we write will be different based on what's supposed to be the intention. Additionally, when developing a suite of quests we need to consider the connections between them. These connections can be as simple as quests being part of a chain (quests can be set to have pre-requisites if you want to do this) or a more complex set of dependencies (it's possible that doing a quest one way makes another quest impossible to do). We can set up whatever connections we like between the quests, or no connection at all if we see fit. Our next 202

Epitaph Online

http://drakkos.co.uk

consideration then is whether our quests are going to be linked to each other, or linked to other quests elsewhere. Pre-requisites for quests can be more than simply requiring that other quests have been completed items required, skills needed, necessary moral codes these are all things you should consider in advance. Ideally you'll have some formalized way of representing this so that your domain administration and other creators can see, at a glance, how everything fits together. Linking up quests make a development seem more integrated, but it also frustrates those players who hit a stumbling block failing to solve one quest locks them out of attempting the next. This is not necessarily a bad thing, but it's something to bear in mind when planning things out. Ideally quests that are linked will have a sensible progression. Quest A may involve a door with an obvious lock, but no key, whereas Quest B rewards the player with a key, but no door in which to use it. There is a natural link here that leads from one quest to the other. If there is no obvious implied link, there should be a direct hint provided when solving the pre-requisite quest. Imagine as an example for this a pair of quests. Quest B involves finding a secret passage, and Quest A involves fixing the mechanism that opens the secret passage. If the secret passage only becomes available after Quest A is completed, then Quest B should hint at that. If it does not, it is very unlikely the player will thoroughly re-explore an already explored area. The mechanism in Quest A should thus be an obvious triggering mechanism for a secret passage, or fixing it should give a hint that something may have changed in the room. Something as simple as echoing a message to the room would suffice for this. The combination of pre-requisites and an understanding of why each quest is in place allows you to decide on what the exact steps of each quest should be.

The Zombieville Quests


We already know the rough theme for our quests each allows you access to a different part of the rural school. Now we need to work out exactly how that's going to work. We also want one of these quests to be harder to find than the others so as to reward explorers. An easy way to do that is to have one quest as a pre-requisite of the others. Our task is to explore the school, which instantly suggests the kind of activities that would be appropriate for the quest repairing staircases, breaching barricades, entering security codes, climbing wrecked furniture essentially the whole gamut of activities that involve people exploring an abandoned civic building. We haven't written a single line of descriptions yet, so we are entirely free to decide on whatever we need to be in place so we can write our rooms around these requirements. If we want one quest to be linked to another one, we also need to bear this in 203

Epitaph Online

http://drakkos.co.uk

mind in what ways can a quest be revealed by the completion of another set of tasks? Unfortunately there isn't a quick way to come up with ideas like this, you just need to trust to your imagination. As such, it's necessary for me to simply leap over the thinking part of that to a firm plan for what we'll do. This is the equivalent of me saying Here's one I made earlier' the process you need to go through to generate ideas yourself is to sit and think, talk over ideas with people, and imagine what you'd find fun if you were presented with the tower as a player. That takes time, and it's not something that can be sped up. Think back to what you're actually hoping to accomplish with your quests, and consider what can be put in place to support that. Let's pretend you've already done that, and that we're ready to outline the result of our thinking. Here's what we're going to do the school is reached via a crack in the wall which leads to the library. Upon entering this room for the first time, the player is confronted with a horrible mess with books strewn everywhere. The bookshelves will be in a fairly sad state of repairs, but marked with category headings. The first quest is for the player to organize the library once more by picking up books, reading the title, and putting them into the right category. This is a quest that is obvious and it doesn't need to fit into any larger master plan. Why they should stop and do this in the middle of a zombie apocalypse? That's not our problem, man nobody is holding a gun to their head. We've got hidden areas we want to unlock in this school, and so we have an option of making access to the other rooms based on the completion of some kind of quest. Or at the very least, we want the completion of one quest to give us the necessary information we need to complete the next. Perhaps access to the school's corridor is blocked by an incredibly well barricaded door, but careful examination of the room might reveal a piece of the wall that is structurally weak (a partition wall, perhaps) that you could more easily break down. While not strictly speaking a secret quest, it's one that'll need people to pay particularly close attention to find. We could base it on a certain level of mental.perception.observation, and require some kind of tool for the player to actually break through the weak area a hammer, for example. It's not conceptually perfect, but it'll do. Upon reaching the main corridor of the school, the last quest becomes to gain access to what will become our instance that could be the headmistress's office, or maybe the cellar. Or the attic. Any of those would be entirely appropriate. In terms of obstacles, we have plenty of options a sturdy locked door, barricades, perhaps even rubble from a roof that never got the needed repairs that had been scheduled. We can mould this to the mental image we have of the school, and choose obstacles that match our conception of the state of the structure. You could have it so that to reach the headmistress's office, you need to clear the rubble that is blocking the staircase to the higher reaches. This too is an obvious quest because an obstacle implies a resolution, which implies a quest. An alternative is to have the rubble as a red herring and the actual quest as something slightly different. A nice way to do this is to provide an add item saying something like Seriously, this rubble is immovable. Don't even try. Honestly', because people will try anyway and you at least warned them. If we are especially cruel, we can add some commands that allow people to attempt to use various 204

Epitaph Online

http://drakkos.co.uk

tools on the rubble to try and clear it but that provides nothing except the cold, hollow laughter of the damned. Whether this is what you want will depend on your own vindictive nature. I, for example, am full of hate and so the rubble will be a distraction and the real quest will be to activate the electronic keypad that blocks access to the cellar. The code to unlock the door will be found in the library itself, provided the library has been put in order. However, because we don't want the player to have to sort the library every time they want to attempt this quest, we will provide the secret code to them as an output of the quest. How that code works doesn't matter right now, we can decide on that later. This will lead to a the service corridor that leads to the cellar and the boiler in the boiler room is our headmistress. We will return to her at a later date. Next, we need to work out the precise steps that are going to be taken to actually solve the quests, and what systems will need to be in place to support them. Before we get to talking about that that though, we need to discuss the difference between linear quests and dynamic quests. This is an important topic because it directly relates to how listable' your quest is, and how likely it is that someone can solve it by reading a solution.

Conclusion
In this chapter we've introduced a new diagramming format for showing quest relationships, and also spoken about the intentions behind quests. We've also refined our plan even further and identified what our quests are about, roughly. Our next step is to outline the steps that must be taken to actually complete the quests. We'll do this without any reference to the code we need we are actually going to code all of this, just not right now. At the moment, our only constraint is on our imagination.

205

Epitaph Online

http://drakkos.co.uk

Dynamic Quest Design


Introduction
The existence of quest lists has been a problem for MUDs for a considerable period of time. At their core, quest lists are a way for an individual to quickly siphon rewards with no expended effort armed with a quest list a new character can easily reach levels of competence that they simply did not earn. Some MUDs resolve this problem by not giving actual in-game rewards for quests, but the usual default attitude (and the one we have on Epitaph) is that we accept that we cannot defeat quest lists. In fact, we have our own quest solution system available from the website. For a long time, using quest lists was considered tantamount to 'cheating', and many MUDs had and have harsh penalties against those who were discovered using them. Providing our own quest solution system simply means that everyone can prosper if they choose, rather than those who have access to hidden solutions. However, just because we have finally accepted we can't beat the problem, it doesn't mean we can't use it as an opportunity to develop better quests. That's what the topic of this chapter is. As a gesture of full disclosure, I will point out that a chunk of this chapter is taken verbatim from the article Unlisting The Listable which I wrote while over at Discworld. It's also available on the Discworld webpage and also published on the articles section of the website Top MUD Sites.

Dynamic and Linear Quests


Linear quests are those that can be solved by issuing a set of commands in the right order, where none of those commands changes from individual to individual, completion to completion. Most of Epitaph's quests fall into this category, and thus most of them are candidates for inclusion in a quest list. Dynamic quests are those in which some elements of the quest are randomized or otherwise changed based on the player attempting the quest. At best, such quests can be listed only in terms of the instructions as to how to solve it the solution is still something that is unique to a particular player. The exact level of dynamism will vary from quest to quest. A quest where an NPC says 'Guess the number', and the number is randomly assigned from player to play strictly speaking, that's a dynamic quest, but it'svery much at the shallow end of the dynamism pool. At the other extreme is a quest like the Murder Mystery in Genua on Discworld (a quest I wrote), where absolutely every part of it is dynamically generated when the quest is started. The only thing a quest list can provide for this is the instructions 206

Epitaph Online

http://drakkos.co.uk

as to how to interact with the quest. Solving the quest, regardless of how much help you get, is still the work of a good few hours of sleuthing. Let's talk about the Murder Mystery and see an example of the architecture needed for an entirely dynamic quest: The quest is set in a hotel a murder has been committed murder most foul! It's your job to find out who the murderer is. The hotel is a suite of eight or so rooms, each of which has numerous hiding places. The murderer is one of seven NPCs. The murder victim is the eighth NPC. NPCs are randomly set up so that one is the victim and one is the murderer. Instantly, this adds a level of dynamic configuration to the quest that makes it more difficult to list. The murderer is furnished with a murder weapon (randomly), and each NPC is setup with a series of occupations each occupation is associated with a given murder weapon. Captain Beefy, head of the city guard, perhaps trained as a butcher before becoming a soldier he'd be familiar with swords (from his soldiering), and meat cleavers (from his butcher days). Again, all of these occupations are set up randomly. This immediately narrows down the suspects to those who are familiar with the murder weapon examining the corpse reveals the weapon used, as well as a number of other clues (generated randomly). These clues are scattered about the hotel rooms, in various hiding places. Each NPC has a randomly generated whereabouts list the murderer will be the only one who was in all the rooms containing clues after the murder was committed. As an extra constraint, the murderer will have been seen by another NPC every hour after the murder. The quest works by questioning NPCs asking them where they were, who they saw, and who they know. Through thorough questioning, you can build up a profile of the movements of each NPC. From this and the list of murder weapons, you can work out which NPC was in which rooms after the murder, and then you accuse them if you were right, you get the reward. If you were wrong, you get nothing. This quest took weeks to code and test but it stands as one that it is simply pointless to add to a quest list there's too much randomness in how the initial setups are generated. There are billions of possible permutations the only bits that can be listed are the instructions, and those are the easy part. The hard part is solving the puzzle.

Which are Best?


There's not a lot of dissent on this score quests that give rewards should require an investment of effort. There is no effort in following instructions from a quest list, and so linear quests disproportionately reward quest listers. It may take a normal player an hour to solve a quest, and a quest list user can type in the instructions, get the reward, and then move on to the next. This completely inverts the principle of invested effort influencing reward.

207

Epitaph Online

http://drakkos.co.uk

Every level of dynamics that you put into a quest adds a level of work that an individual has to do in order to complete the quest. As such, whenever possible, you should be thinking of ways in which your quests can be randomly set up. HOWEVER! It is important that a quest system offers something for everyone, and the kind of quests that involve people grinding out calculations, poring over spreadsheets and ticking off possibilities on a grid are usually those that emphasise logical skills over other skills they have to be, as a nature of their dynamic construction. Some people don't enjoy sudoko some people don't enjoy crosswords. Those people are not well served by quests that are heavily dynamic. Quests that have linear steps are not necessarily 'easier', and do not necessarily exercise the other skills that people may have, but the offer possibilities for more creative exercises because there is no need for the experience to be resolved down to easily represented and manipulated data points. Once, I felt very much that a MUD should have all dynamic quests all the time. Since then, my thinking has matured a bit. There's no need to be militant about this a good game can, and probably should, have quests of both types. The question becomes again though how do you ensure that people get rewarded appropriately for their effort? For this, we have simply made the generation of quest levels based on a rating in several categories how random is it, how much player skill is required (that's skill on your behalf). How much character skill is required (your character's levels), how much travelling, how much time, etc, etc. These then get thrown into a big blender and out comes a number that represents the quest level easily listable quests thus simply end up being lower level quests than those that need you to spend half an hour with a spreadsheet. So, the policy on Epitaph is schitzophrenic I do prefer dynamic quests on the whole, but I also appreciate my preferences are not universal and that others will have different perceptions. The most important thing is that your quests are interesting.

Some Guiding Principles For Dynamic Quest Design


The process of making a dynamic quest is the process of creating an implied contract with your players that their effort is going to be fairly rewarded. As such, there are some design principles that go along with putting such quests in the game. Principle One: Be Transparent If something can be listed, it will be listed. Pre-empt this by making all of that information freely available within the game. You are not deterring anyone by 208

Epitaph Online

http://drakkos.co.uk

hiding it - secret information is valuable, and it only penalises those willing to play the game fairly. Obviously this contradicts our discussions about the things that motivate explorers, but everything in game design is a trade-off, especially when the thing in question has broader appeal everyone does quests, in general they can't cater purely for explorers. Be up-front about the syntax make it obvious, and make it clear. Stick it in a help-file, or on a web-page. Let everyone know how to find it. Principle Two: Identify areas where there is room for randomness Consider the standard 'courier' quest (the kind that we do via missions): You are a brave hero, yadda yadda yadda, great risk, peril, danger, take my item X, give it to NPC Y. The bits around the edges may be different from quest to quest, but the structure is the same. There is great scope for randomness generate item X from a list of possibilities, and likewise generate item Y from a list of possibilities. The same quest engine can generate dozens of different quests: 'Take this rock to Maggie May', 'Take this pencil to the Consumer of Souls', 'Take this sword to Mrs. Miggins'. This is such a simple example that we don't even consider it a quest it's a faction mission instead. All that someone needs to do to make their very own faction use it is to define some enemies (used to determine killing targets), some random macguffins (items that you have to take from place to place), some desired items and so on. The framework of these is so simple that our mission generator can handle everything else. We have a quest that involves decrypting a coded message on behalf of a university lecturer. Obviously this message could be chosen from a list, but we did something slightly different the message is only available as a piece of substitution ciphered text. The principle of deciphering such a message is fairly standard, and a description is available on many web-sites but it's still a complex thought exercise to put that theory into practise. An alternate mechanism for this kind of quest would have been to give the text coded, but provide a 'decipher' command that took a character's skill and gave them the answer. The 'story' of the quest is the same, but the first way is dynamic and the second way is linear. For the former, only the instructions can be listed. For the latter, everything up to and including the commands you need to type in which order can be put on a list. Principle Three: Provide in-game means of finding quest hints Players often turn to quest lists in frustration not being able to find quests, or simply not knowing what to do. Documenting all the bits of the quests that are possible to list helps alleviate this, but so does providing an in-game quest hint system. Does your area have bars and taverns? That's an excellent place to drop 209

Epitaph Online

http://drakkos.co.uk

the occasional hint. 'I hear there have been some murders up at the old hotel', or 'I saw a shady looking guy hanging around in the bar the other night'. You can liberally include red herrings and misinformation, as well as some useless trivia or funny observations. It doesn't have to be All Business. Make it funny, make it cute, and people will like it regardless. Guess what we have on Epitaph to help you with that? That's right, a rumour system! Simply giving people an indication of where quests can be found removes a lot of the motivation for those 'casual' quest list users those who didn't know where else to look and so turned to the Dark Side in their despair. If we make quests easy to find, and provide hints as to where people should be looking, then those who want to solve the quests themselves are able to do so without being entirely disadvantaged. We offer both 'OOC' mechanisms (the questsense command and the quests page on the website), and 'IC' mechanisms (rumours seeded through the game). Principle Four: Make it Interesting Obvious, I know... but hear me out. Remember why people do quests in the first place sometimes it's for the reward, and making a quest unlistable means the reward can be proportionate. Often though, it's just for the thrill of solving a puzzle. Unlistable quests are a quantum leap more difficult to code, but they offer the potential to be so much more interesting. Their very difficulty inspires us to be more creative. The more interesting you make the quest, in terms of the tasks involved and the storyline, the more people will be willing to do it. Principle Five: Amply reward effort If it takes two hours to solve a quest, it should give a substantial reward that reflects the time taken. It doesn't have to be equal to what would have been gained farming NPCs for XP, but it should be enough to justify the expenditure of time. If people feel that they are getting enough of a reward for their hard-earned quest, there is less of an incentive to attempt to buck the system. The investment in coding effort means that you want to get maximum utility out of your quest a fair reward is also a good draw of interest. If you make it worthwhile, then your development time will not have been in vain. For the most part, the rewards a quest gives our are managed centrally, but you should consider how much you are asking of your players, and whether or not that is commensurate with the reward they will get in the end.

Dynamic Quests The Book Sorting Quest


So, how does all of this help us design our Zombieville quests? Well, for one thing 210

Epitaph Online

http://drakkos.co.uk

it gives us another design principle to bear in mind make it all as random as possible. Let's go over each of our quests in turn and look at how we could make them dynamic. The first is the sorting exercise in the library. The traditional, linear version of this would be to have a set of books that fit into a set of bookshelves. The first book is 'Stuff for people' and fits into the bookcase 'Things about stuff'... if it's always that, then it can be solved entirely from a quest list. A better solution is then to generate books either randomly, or from a large list. There are opportunities here to make the quest more 'fun' or interesting too in the choices of book you make. You can lighten the atmosphere by including books that are in the vein of the old standard of 'Amusing Book Title' by 'Amusing Author', such as 'A Stitch In Time, by Justin Case', or 'A Boy's Guide to Archery, by Beau N. Arrow'. On the other hand, if you're looking to intensify a dark atmosphere, you can have books that play to that them famous books about the dreadfulness of human nature, or even fake books that do the same. 'She Won't Be Coming Back, A Mother's Story of Loss and Remorse'. It's a bit of a downer, but the ornamentation you put around a thing like this is important for the atmosphere you build. It's little touches like this that make our game so special, and worth exploring. If we have a long list of books, and a set of randomly selected book cases, then we're a long way towards producing real dynamism in our quest design. To imagine this in practice, let's say we have four bookshelves 'biographies', 'fiction', 'geography' and science'. Those four bookshelves then set up the possible set of books to be found in the library. The player picks up a book, and reads the cover. The book is randomly chosen from the list of valid options: 'The Principle of Entropy Thermodynamics and the Decline of the Inevitable Universe'

The topic of that is science, and so the book is slotted into the science bookshelf. Repeat this for a certain number of books, and the library is organized. The bigger the list of books, the less chance there is that the books will be listed on a site. You don't even have to have every one of them being a joke or a deep philosophical statement, you can have the book titles completely randomly generated if you so choose. Indeed, we're going to go with a variation of these when we move on to actually writing the quest. So, let's formalize what this quest will involve: Every reset, the library subsides and the books spill out. At this point, the bookshelves in the room are randomized. The random bookshelves are used to generate random book titles. When a player picks up a book, they have to put the book in the appropriate shelf. 211

Epitaph Online

http://drakkos.co.uk

When they've done this ten times, the quest is completed. Upon sorting the last book, a small slip of paper falls out of it, containing a randomly generated code which just happens to be the code to the basement.

While we don't need to come up with all the quest details now, we should probably think of a quest title and quest story in advance. For this one, how about something like: Visiting Librarian, in which you obeyed the categorical imperative Your quest story should ideally be a little bit funny, or a little bit dark. At its best, it would be both dark humour is pretty great for a game like ours. It's not either of those things here, because I am an Old Man with no measurable sense of humour.

Dynamic Quests The Partition Quest


It's a little harder to figure out the dynamics of the partition wall quest because it seems like something you either do or do not. That's absolutely fine as I said above, a blend of linear and dynamic quests is better than all one or nothing. Having said that, there are ways and means to make the quest more dynamic, and we'll do these because it'll be more instructional when we come to actually code it in Intermediate LPC. First of all, we could link the location of the wall to random bookcases, so you need to look in the right place before you see that part of the wall is a partition. Rather than 'look wall' you would 'look wall behind science bookshelf'. In this way at least you have to do some work yourself. The second option is to base the tool required to break through on some kind of random algorithm sometimes it'll look as if a hammer is needed, sometimes a crowbar, maybe other times it'll need a knife of some kind. This should remain constant as soon as a player has discovered the hole, because it's unfair to make players go fetch a crowbar and have the room reset in the interval so as they need a shovel. So, our formal steps for the quest are as follows: Randomly generate a location for the partition wall If the player passes an observation task check, show the wall when they look in the right place. When a player looks at the wall, give them the clues as to how they need to break through it. The player uses the hints given as to tool needed to break through the wall. Upon doing so, the quest is granted and the player is moved into the corridor of the school.

For a quest story, how about this? 212

Epitaph Online

http://drakkos.co.uk

Mister Gorbachev, in which you tore down that wall I know, it's terrible. I'll get my coat...

Dynamic Quests The Secret Room Quest


The randomness in the cellar is already in place if the secret code generated from Visiting Librarian is random, then every player will need to complete that quest in order to find the code. Let's add a little bit of extra challenge though we'll hide the keypad behind a painting of an old school headmaster. We can add extra randomness in by having more paintings in the room, and changing which painting the panel is behind for each player. You could even work this as a simple puzzle in the vein of the zebra problem if you were feeling adventurous. We'll be slightly less adventurous and having the paintings be of people with particular facial features and algorithmically generated names. We can generate them entirely randomly so as to end up with, for example: The huge portrait of a man with a big nose and blue eyes. The small portrait of a woman with small lips and red teeth The medium portrait of a bear with big teeth and green eyes

Ideally this would link back into the library, so that a player making use of the library would be able to find the clues necessary to identify exactly which painting needs to be moved to reveal the panel. Our formal steps then: Randomly generate a set of paintings. Allow the player to research the secret panel in the library and find clues about the paintings. When the player moves aside the right painting, they find a panel. Entering the secret code into the panel opens up the secret door to the hidden staircase.

And our quest story: Art Investigator, in which you discovered what was beneath the surface. Look, stop staring at me. I'm doing the best I can.

Conclusion
After three chapters, we now have an overview of exactly what our quests are going to involve. You may be thinking 'I don't know how I'd even begin coding any of this', but that's okay. You will when we get to actually starting to write up our new and improved village. We've spent a lot of time talking about quests specifically because they're one of 213

Epitaph Online

http://drakkos.co.uk

the easiest things to get wrong, and it's often hard to relate the often vague guidance you are given on how to code a quest to our own specific requirements. Sadly, most of what quest design involves is just sitting and thinking, and there's no shortcut for that. However, there are plenty of places to look for inspiration your domain administration, your fellow creators, relevant books, the playerbase, and as an absolute last resort when you have no other alternative, you can even talk to me!

214

Epitaph Online

http://drakkos.co.uk

The Ten Commandments of Room Descriptions


Introduction
We shouldn't get bogged down in the complexities of planning features it's part of being a creator, but so is the work of actually providing interesting, clear and entertaining descriptions for everything that we write. Indeed, certainly in our first few developments this is usually what we spend the majority of our time doing. It may seem like something that is self-evident with regards to how it should be done, but over the years we have built up numerous conventions as to how descriptions should be written. In this chapter we're going to talk about what needs to go into a good description it should be noted that everyone has their own views on what Good Writing is supposed to look like. Your domain administration will be able to give you guidance on what is considered acceptable within your domain. On top of these though, there are some guidelines that are Universal. These are the ones we're going to discuss.

The Ten Commandments


The Ten Commandments of Room Descriptions was a simple set of guidelines I wrote as to the things you should and should not do when writing descriptions. They are incorporated here from the original article. I. Thou Shalt Not Use 'You' In Room Descriptions All direct references to the player should be removed from the description. This is especially true of descriptions that begin with 'You are standing in...', or similar. Room descriptions should make sense from all perspectives and all situations... what if I'm not standing there? What if I'm sitting, lying down, or hopping on one leg? What if I'm looking into the room from an adjacent room, or scrying into the room from miles away? In all these cases, the room description will no longer be accurate:
> look down You are falling at great speed towards jagged rocks below. > /scream in fear You scream in fear. > shout Help! Help! I'm falling to my death and need someone to tell my wife I love her!

215

Epitaph Online <several uneventful seconds pass> > /think You think carefully.

http://drakkos.co.uk

> look You are standing at the top of a cliff. It's perfectly safe, provided you don't take a wrong step. > say Oh yeah! You exclaim: Oh yeah!

It is jarring when this happens, and you should consider this an Inviolable Rule as to your own descriptions. Never make any assumptions on how the player is viewing the description keep it general. II. Thou Shalt Not Assume a Course of Action from The Viewer A room description's purpose in life is to describe - hence the word, description'. What a room description is not there for is to dictate the action of the viewer, even for the holy purposes of narrative causality.
You decide to try the door handle, but the door is locked. You decide to follow the path to the north.

Let the player choose what they want to do, based on your descriptions... you're not here to play the game for them. Likewise, don't tell people how they think, or how they feel - they know better than you do. This is something that you see particularly in games where the courses of action through the game are more restrictive than ours - the MUD is not a choose your own adventure book.
This is a dark, scary trail through the forest. To the north is a horrible looking path, and you decide to follow it to... > don't follow path What? ... to follow it into the depths of the forest where the horrible spiders roam with... > stop following path What? ...roam with their sharp, pointy teeth and horrible eyes glaring... > say I don't want to follow the path, I'm scared of spiders! You exclaim: I don't want to follow the trail, I'm scared of spiders! ...horrible eyes glaring, and yet you feel unfraid, as if the spiders hold no fear for you. > /whimper in fear You whimper in fear.

Having the game assuming a course of action from you is tremendously annoying 216

Epitaph Online

http://drakkos.co.uk

if you are personally invested in your character. It can seriously impact on a player's sense of immersion. III. Thou Shalt Not Write Static Descriptions of Dynamic Objects By dynamic objects, I mean those objects in a description that are likely to change their state or appearance during the course of the game. 'The door to the north is closed', for example, when the door may be opened by a player or NPC. Or... 'The chairs are empty', when someone could sit in them.
This is a lovely pre-apocalypse pub, with an empty stool at the bar. Drakkos Wyrmstalker arrives from the south. Drakkos Wyrmstalker sits on the stool. > look This is a lovely pre-apocalypse pub, with an empty stool at the bar. Drakkos Wyrmstalker is sitting on the stool. Drakkos Wyrmstalker says: Hey sailor. Buy me a drink?

Static descriptions should refer to static objects. If you must make a reference to an item that is likely to be dynamic (like a crowd of people), then make it vague rather than specific:
> look crowd The crowd mills and jostles around you, caressing your body with the Brownian motion of its constituent members.

Likewise, when describing an object that is mobile, it is better to do this as a room chat. The bird is flying in the sky' in a long description makes the whole room look static, like an oil painting. If the bird flies across the sky occasionally in a room chat, this is less true. IV. Thou Shalt Not Use Relative Directions Unless you're going to get very clever with code, you cannot assume a direction of entry from a player. When entering a room with two entrances, in general you don't know which one they entered by. Likewise, in a room with only one entrance, you don't know if they arrived via that entrance, or if they portalled in, logged in there, or were dropped off there by mutant bats. For this reason, you shouldn't assume that a relative direction is going to be appropriate.
The forest stretches ahead of you to the north.

What if I just arrived from the north? Wouldn't it be stretching behind me? 217

Epitaph Online

http://drakkos.co.uk

Likewise with 'left' and 'right'... these will change dependent on what direction I have just arrived from.
This is the kitchen of a pretty house. The larder is to your left. The sitting room is to your right, the pantry to the south and the hallway is behind you to the north. > south It's the pantry. > north This is the kitchen of a pretty house. The larder is to your left. The sitting room is to your right, the pantry to the south and the hallway is behind you to the north. > ponder You ponder. > north It's the hallway. > south This is the kitchen of a pretty house. The larder is to your left. The sitting room is to your right, the pantry to the south and the hallway is behind you to the north. > shout Help! Help! I'm trapped in the house that Escher built!

Avoid words that assume you arrived from a particular location. It won't bother all players, but the ones it will it will bother a lot. V. Thou Shalt Write (Mostly) Proper English Although writing a description is not the same as writing a thesis, there are certain stylistic elements of formal writing that you should keep in mind when writing. One of these is that you should not write numbers as Arabic digits... instead, write them out fully. 'There are 2 large stone blocks here' should instead be 'There are two large stone blocks here'. This rule extends at least up to ten, and usually up to twenty. Beyond that, using the digits is acceptable, although using a general plural such as 'lots', or 'many' is perhaps a better approach. After all, who is going to count the exact number upon a casual glance at something? Also with regards to writing properly: proper sentences have verbs, and so should all the sentences in your description. 'A large clearing' is not a sentence while 'This is a large clearing' is a sentence. However, don't worry too much about being too formal... in many cases, writing completely formal text will detract from a quality description. Use formal writing where appropriate, and whatever sounds good for the rest. Finally, the standard of the MUD is for the British spelling of words. Color, center, and emphasize, etc, are all wrong. Colour, centre and emphasise are correct. All your descriptions should conform to this standard. If in doubt, grab hold of a 218

Epitaph Online

http://drakkos.co.uk

British English Spellchecker and run it over your descriptions. VI. Thou Shalt Make Thy Text Easy On the Eye A MUD such as Epitaph is, I'm sure you've noticed, a text-based medium. As such, the presentation of the text in a clean and attractive manner is of paramount importance. Although the practice is falling out of vogue as variable width computer fonts become the norm, it is one of the standards of the MUD to double-space between sentences. The practice of double-spacing stems from the days of typewriters when each letter took up the same amount of space on a page. Many MUD and telnet clients still use fixed-width fonts to present MUD output, and using only a single space between sentences makes the whole description cramped and difficult to read.. Related to this, you should avoid the use of colour in descriptions and shorts. Colour is a powerful method for emphasising particular words or sentences, but using it carelessly makes everything garish and detracts from the rest of the text. Additionally, it detracts from the consistency of the MUD in general. Why does your 'red sweater' have a coloured short description, when someone else's 'green sweater' doesn't? Remember too that a number of users cannot see colours on their clients, and even those that can may find your choice of colour clashes with their client's background or their own defined colour schemes. Finally, using colour also means people have to worry about stripping colour codes from your short descriptions when they reference them in code... an additional CPU and design overhead for something that is usually undesirable in the first place. VII. Thou Shalt Describe Every Noun If you mention a noun in your room, you should also make sure you have an add_item for it. This goes equally for nouns within the descriptions of add_items. There are few things more instantly indicative of a lack of attention in a MUD than descriptions that mention lots of things in the long description, but never provide items you can look at for them:
This is a nice stairwell. There are some lovely stairs leading upwards. There are zombies on the stairs. > look stairs Cannot find "stairs", no match. > look zombies Cannot find "zombies", no match. > /sigh You sigh

Of course, you don't have to be too fanatic about this... describing the dew drop 219

Epitaph Online

http://drakkos.co.uk

on the leaf of the stalk of the plant in the garden is unnecessary unless you really feel you want to. Most people will never look beyond a certain depth of add_item, but it's nice to reward those that make the effort by ensuring they have something to see if they're following your descriptions. Having a little joke at the end of a long chain of nouns is a nice touch too... it's the attention to detail that really makes a MUD special. VIII. Thou Shalt Not Describe NPCs In The Description If you have an NPC in your room for whatever reason, it should be a separate object and not a part of the room description. So rather than writing the wiry old shopkeeper with an add_item, he should be a separately coded NPC. If you don't do this, you get all sorts of inconsistencies such failing to find a match when you try to kill them, or being told they are not a living target when spells are cast on them. It's also inconsistent with how the rest of the MUD deals with NPCs... where there is a living creature, you should be able to interact with it as such in the same way as you can elsewhere.
This is a shop. There is a shopkeeper standing here. > look shopkeeper. What a clean old man! > /leer shopkeeper You leer shopkeeper. > /ponder You ponder. > kill shopkeeper. Cannot find "shopkeeper", no match. > /boggle You boggle at the concept.

You may not want it to be possible for players to kill your precious NPCs, but that's irrelevant there are other ways to do it than to make the NPC part of the description. It is inconsistent for there to be a mention of an NPC and no way to interact with it. IX. Thou Shalt Not Put Command Hints In The Description If you are describing a room with special commands within, it's very tempting to hint at these commands in the room description. This is Very Ugly, however, and should be avoided:
This is a nice shop. You can 'buy' and 'sell' stuff here

It's much, much better to put these command hints in a help-file. Most standard shops will already have a help-file, so you won't even need to do that. However, if your room or shop does something unusual, it's always better to give the syntax in a help-file rather than in the long description. Partially this is to make the help system a homogenous, consistent thing. It's also to remove Out Of Character 220

Epitaph Online

http://drakkos.co.uk

game information from the MUD. Signs are also a valid way of providing information relating to how a particular room or shop works... but signs should always be in theme, prompting as to the use of a room, but never directly quoting the syntax. Signs are most effectively used as a means of conveying game information relevant to a particular room, such as exchange rates, cost for services, and so on: Bad:
Welcome To Bing's Bank! We Give You More Bang For Your Buck! You can 'apply' for an account here. You can 'deposit' money here. The transaction fee is 10%. Shop around! We guarantee you won't find a better rate anywhere else in this bank

Better:
Welcome To Bing's Bank! We Give You More Bang For Your Buck! Apply For An Account Today! We charge a low, low 10% on all deposits. Shop around! We guarantee you won't find a better rate anywhere else in this bank.

Just think how the signs in your local store, bank or supermarket look. That's the way all signs should look on Epitaph. Leave the actual commands and instructions for the help-file. X. Thou Shalt Have Fun With Your Descriptions This isn't just for your benefit. It's far, far more interesting and enjoyable to read descriptions written by someone who is enjoying what they do, rather than descriptions that have been mechanically churned out according to some rote formula. However, don't go nuts with this. It's all too easy to just give in and be silly or surreal in the hopes it will make your descriptions funny and enjoyable. What it mostly does, however, is make you look like a loon. In all cases, try and keep your descriptions in theme, but fun!
This is a large marketplace. Giraffes are bouncing on the stalls and pink elephants in tutus are waltzing gently around the villagers as they shop. > look giraffe

221

Epitaph Online There isn't a giraffe here... you're going crazy! > look elephant Why would there be an elephant here? It's a marketplace. > /roll eyes You roll your eyes.

http://drakkos.co.uk

Remember your theme here there is nothing at all wrong with dark comedy gallows humour is a regular feature of horror and apocalypse works. But we're not a game with a 'silly' theme we're trying to construct a particular kind of atmosphere, and you must be mindful of that.

Conclusion
So, those are the commandments for writing room descriptions. In the next chapter we'll look at it more in terms of how to put descriptions together. Pay attention to these commandments and you'll avoid making most of the mistakes that mark out a new creator from a more experienced creator, and that's good part of the reason you are reading these documents is to learn from everyone else's mistakes!

222

Epitaph Online

http://drakkos.co.uk

The Art of Writing Room Descriptions


Introduction
Room descriptions are not the simplest things to write from a cold start, and are often a stumbling block for new creators. It takes a certain amount of mental exercise until you are able to write hundreds of descriptions without feeling overwhelmed at the task. Having a semi-formal process for writing is the way to gain sufficient confidence to be ready to describe anything you need, and in the quantities your require. In the last chapter we discussed the commandments, but in this chapter we're going to take a more positivist approach to writing descriptions how to get started, and how to be sure that they're good enough.

Your Mental Picture


The first step in writing descriptions is to picture what you're writing about. If you're describing a room, imagine what it looks like get a feel in your dusty mind for the most distinctive features. A good room description is around fifty to eighty words in length. That may sound like a lot, but it's not really - in fact, once you get a taste for writing them you'll find it's quite restrictive. The paragraph above is seventy-four words in length - you'll soon use up the word allowance before you've really described everything you want to. What's useful then is a structured way to write the description so that you can channel your descriptive juices more effectively. To begin with, consider what you can see in the far distance this helps set the constraints of your 'observable view'. When out in the Wide Open, you'll have a far greater vista than you would in a cramped city, and you should reflect that in your description. Setting the context is a good way to begin a description where you have great visibility, your description should begin by explaining that. Likewise when within tight conditions, the description should reflect that too.
The trees here blot out the sky with their densely packed branches. All around, the forest is dark and forbidding... little light penetrates the shadows within.

Next you can focus on the main features of your immediate location what is it that distinguishes this room particularly from any other room in the MUD? Close your eyes and imagine standing in the room what instantly strikes you that you should mention? You don't have to describe absolutely everything in the long description, just those elements that are immediately noticeable. Are the walls a particular colour? What about the floor? Are there windows? You should frame such details in a way to describe the constraints of the room the bits that relate 223

Epitaph Online

http://drakkos.co.uk

to where this room begins and ends.


The ground is strewn with organic matter shed from the trees above, forming a carpet of leaves and wet, moldy bark.

Next, make mention of any items of particular note in the room. Does it have bookcases? Does it have a fountain? Is the ground strewn with rocks? Imagine yourself as a burglar casing the joint which would be the things most likely to draw your eye in terms of the contents of the location?
Several large rocks have been set in a circle on the ground here, and there are several vaguely humanoid-looking wooden puppets hanging from the nearby branches.

Finally, relate the room to the rest of the area. The exits line of a description gives you the available exits, but you can expand on these to provide actual, immersive detail. Don't write 'the exits are north and south', because that duplicates already available information. Instead, you could do something like the following:
The road north leads into a clearing, while the path south plunges into the heart of the forest.

Bringing these together gives you the full room description:


The trees here blot out the sky with their densely packed branches. All around, the forest is dark and forbidding... little light penetrates the shadows within. The ground is strewn with organic matter shed from the trees above, forming a carpet of leaves and wet, moldy bark. Several large rocks have been set in a circle on the ground here, and there are several vaguely humanoid-looking wooden puppets hanging from the nearby branches. The road north leads into a clearing, while the path south plunges into the heart of the forest.

While the structure is important, what is also important is your choice of words. You should pick words that are evocative of the impression you are trying to convey. Be careful though not to exhaust your vocabulary! The word choice in our example description here reflects that this is a scary forest, and not a cheerful Disneyesque one. Mention is made of the darkness, and the forbidding atmosphere. The trees are portrayed as being active in blotting out the sky, rather than The sky can't be seen because of the branches'. The path into the heart of the forest doesn't lead into it, it plunges like a dagger. You have to be careful with this kind of thing, because if you do it too much your description just sounds ludicriously over the top. Evocative phrases are to be peppered through your description like a delicious, but spicy, herb. Consider what happens when we turn the evocation up to eleven:
The trees loom all around, their vicious branches ripping at the sky like wooden talons. The forest all around emits an aura of death and despair, promising only horror for those who dare explore its interior. The ground is a wet blanket of rotting leaves and decaying bark. Jagged rocks have been set in an ominous circle on the ground, and wooden puppets have been strung from the branches like grotesque parodies of humanity. The road leads north into a clearing, with the path south leads into the darkened horrors of the interior.

224

Epitaph Online

http://drakkos.co.uk

Individually there's nothing especially wrong with any of the description, but when taken together it screams I am trying very hard to scare you!', and the entire effect is lost. This leads onto a not insubstantial secondary problem... when it comes to writing the next description, how are you going to give the same impression without repeating yourself? A common mistake that new creators make is to go overboard with adjectives. Not everything you see is interesting enough to justify an adjective:
The beautiful street is sparklingly clean. Busy people go by carrying interesting-looking bags of shopping. Cheerful dogs bark with happy woofing sounds'

It might look okay, but as with our over the top' description it just doesn't ring true - it's somewhat artificial in its insistence that everything in the room is worth actually describing. You can't make banal interesting, so don't try - don't force adjectives just because your high-school English teacher told you that they were important. Keep them in reserve for when they're actually warranted. Finally, if you have a strong mental image you'll be able to appreciate that while you know what things are, people coming to your description without that context will be looking at a scene they can't neccessarily parse. 'People wander into the nearby bank' is probably fair enough, but how do the players know it's a bank? People wander into the ornate building' is a little bit better, with the clue that the building is a bank reserved for its add item. You can get additional flavour in a description by making use of active verbs over passive 'the shop dummy has a hat on it head' may be true, but is boring. 'the shop dummy has a hat perched on its head' is better, but you can continue to refine this until it says something genuinely interesting, such as 'the shop dummy has a hat perched jauntily on its head'. It's hard to keep this up, description after description, item after item, but you soon get into the swing of it. The key is to have somewhere interesting to describe. Anystreet in Anytown isn't going to be easy to describe because it's not exciting. That strikes at the core of the main difficulty of room development on Epitaph we don't live in an amazing fantasy world, we live in a world that is filled with mundane locations and the same branding colours and logos spread everywhere. That's a problem, but it's not an insurmountable one. If you find it hard to write your descriptions, spend a bit of time thinking about whether you can put some kind of feature throughout if. Maybe it has a river flowing through it, or it is nestled into a fertile valley. Having some kind of geographical feature to focus on can make everything flow a little easier. Remember, you're not describing places that have been defined by anyone else, you can make them as interesting internally as you like (within the context of the theme and the constraints of the real world).

225

Epitaph Online

http://drakkos.co.uk

Hand-Crafting versus Procedural Descriptions


One of the most telling experiences I had on Discworld was when the redesign of Ankh-Morpork went live. It's no secret that I thought it was a terrible mistake, and that it marked a turning point for the worse in the MUD's history. But I was at least sure that the players would hate it as much as many of the creators did. I was disappointed that never turned out to be true. And then, when I stopped being disappointed, I started to become depressed because of the lesson I had learned. There were many reasons to hate the New AM project. One of them was the fact, in my mind, it lowered the quality of the rest of the MUD by replacing a 100% coverage of hand-written descriptions (of varying quality, it's true) with huge swathes of generated descriptions that consisted, in their entirety, of lines like 'Street X continues to the east and west'. I hated that. I thought it was the absolute worst advert to give as to the quality of Discworld MUD. I thought people would see that and think 'the lack of care and attention shown here in Ankh-Morpork leads me to believe the rest of the MUD will be as shoddy'. For context, at the time we were putting the finishing touches to Genua, which had taken a long time of writing to put in place. We worked hard on ensuring that it was all properly described, and a lot of care and attention was poured into making it as detailed as we could. I thought at the time that was a good investment of developer effort, and I was very proud of the quality of the writing across the majority of the city. New AM changed my view on how worthwhile that effort turned out to be. What New AM told me was that, while a minority of players do read all the descriptions and pay attention to all of the add items they can look at - for a significant proportion of Discworld's playerbase, descriptions simply do not matter in the slightest. Considering how long a well described room takes to make (a long time), I have come to the conclusion that hand-rolled descriptions area mug's game. Now, that comment needs some qualification, because in its unadulterated form that comment implies 'so don't bother writing any descriptions'. My view lies between the two extremes - rooms should have descriptions, and they should have all the add_items needed to ensure they permit exploration and the construction of a mental picture of the area. However, it's not neccessary to have a unique, hand-rolled description for each room and each item in each room. Quite simply, that's too much to write and too much to read. On the other hand, I do pay attention to what other MUDs are doing, especially new MUDs. Many of them advertise thousands of rooms, but these rooms have no descriptions at all - they are just expressed as locations in an ASCII map. More still have long descriptions, but no add_items. To me, a MUD doesn't look like it's serious about creating an impression of a world unless you can look around you and construct your mental picture. I like to think of the distinction between graphical MMOs and MUDs as the same distinction between books and movies. 226

Epitaph Online

http://drakkos.co.uk

The mind is the best graphics card that you will ever possess, but it does need information in order to construct the world. It's easy to get carried away when writing descriptions though - what's important is that you are sure why you are writing them. To my mind, you are not looking to exhaustively describe a room or an item or an NPC. I hate descriptions that are largely recitations of an inventory, and while I admire the dedication that goes into describing every whorl and sworl in an add_item about a rug, that's not what I want. I want the data points I need to construct my mental representation of the world. I don't want to see three hundred words describing every scratch in the parquet floor - I want to be told 'there is a worn parquet floor' so I am in control of filling in the blanks. In short, descriptions have a job to do - they are for setting the scene and giving people the information they need to visualise their game world, but not confine it. What we've settled on at the moment for Epitaph is a medium point between 'no descriptions' and 'exhaustive, unique descriptions for everything', and it consists of: Streets get a single description that describes them in the abstract Individual buildings get hand-rolled descriptions insofar as they have things of interest Templated rooms have stochastically generated descriptions.

For streets, this raises the additional complication that it's difficult to write a single description for a room that, for example, winds around a larger building. To get around that, we layer landmarks on the description that so that you can see where major buildings are in relation to where you currently stand. Each landmark comes with a number of descriptions, additional add items and such that ensure that you can see an appropriate description based on where you are and how far away it is, and you can look at the extra details that are indicated in the add_item for the landmark itself. Each of these landmarks are pretty detailed, and the combination of landmarks you can see from a room define what your description currently is, and the detail of the room, and the range of add_items. Individual buildings get hand-rolled descriptions, but only where it makes sense. Two halves of one large, uniform room are likely to share a room description, only varying in terms of specific details. By this I mean, for example, a long dining room which may be made up of three rooms, but each room is pretty much the same. Or a long wood panelled corridor where there are no distinguishing features at any particular part of it. Templated rooms are those that we place many copies of - coffee house franchises and houses are examples of these. Within the code for these, we use a stochastic system to set up the descriptions (colour and type of walls, floors, etc) so that they remain constant between loadings of the rooms, but the individual parts of the description will still be hand-rolled and combined together. Thus, one set of options may describe potentially hundreds of small variations of the same core. Does this approach work? I don't know, we'll need to get a lot more people 227

Epitaph Online

http://drakkos.co.uk

through the game to say - I think it does, but a large part of the reason why I think it works is based on the theme. It is partially on Epitaph a commentary on globalisation in addition to a simplification of our work as builders. Every Starbucks looks the same in real life (within some general parameters), so every Starbucks looks the same on Epitaph. Within a unique area though, the situation is different. Alphabet Street may have a single room description, but the Winchester has unique descriptions for each room because they are all different, look different, and are used for different purposes. It's fine to include variations on a theme in multiple room descriptions with a unique area, but shy away from direct repetition. This is especially true too of special words' in a single room description while there are always words you'll need to repeat, the defining words in your description should be used once only. Otherwise, it jars people out of the narrative because it brings back memories of the first use of the word. Sometimes though you're going to get stuck and be unable to provide a new way of referencing something in an interesting way. In such cases, you can consider going off on a tangent by not describing the thing directly but instead approaching it from a different angle.
Imagine trees of such beauty and splendour that simply being in their presence is enough to make someone fall to their knees and praise the Gods that they were alive to experience such wonder. Imagine those trees, because they're nowhere to be found in this forest. These trees are just like all the other sad examples you've seen so far in this desolate place.

Don't overdo it, it's a nice surprise every now and again but soon becomes tiresome as a narrative device. Flippant or jokey descriptions might work well the first time they are encountered, but they become progressively more irritating as familiarity grows. You don't want to be the creator who writes the descriptions at which people roll their eyes. The rule for uniqueness doesn't extend to add_items, although having a number of different add items for the same thing is always preferable. The more variety the better, but you get much less out of completely unique add_items than you do out of completely unique long descriptions. For one thing, the shape and length of a room description is a navigation aid for those who wander around with verbose descriptions on. Even if it's not being used for navigation, more people are going to see your long descriptions on a regular basis than will see the items in the room.

Room Chats
Room chats can be one of the most challenging things to do properly because they repeat so often in a room. A bad room chat will greatly reduce the overall 'polish' of your room, and having a small set of highly repetitive chats can be worse than having no chats at all, especially in rooms where people may spend a 228

Epitaph Online

http://drakkos.co.uk

lot of idling time. Long descriptions and add items shouldn't include activity. The long and athe dd items are, by their very nature, frozen in time. Any activity you describe is likewise frozen in an entirely artificial way. Any motion you want to describe should be migrated to the room chats. Imagine the following in an add item:
Every now and again there is a soft thump as an acorn falls from a nearby tree.

That looks horrible, and not just because of the writing. It just doesn't feel active it feels like you're looking at a painting of a tree dropping an acorn, rather than experiencing something in a living, breathing game world. On the other hand, if you occasionally see as a room chat something like:
There is a soft thump as an acorn falls from a nearby tree.

Suddenly that frozen activity becomes something active that adds a level of richness to your location. The exception to this as a rule is when your dynamic activity is an on-going affair. The hustle and bustle of a crowd can be conveyed in the long, and in an add_item, and also in room chats. The static descriptions should describe the activity in the abstract, and the room chat should describe time-sensitive specifics. You could have for example the following description:
There is a great hustle and bustle of people going to and fro, and here and there.

Then in your room chats, specific examples of that hustle and bustle: An old lady bumps into you, apologises, and then wanders off. A well dressed man stands on your toes, but walks off without noticing. A small child wanders past and laughs at you before wandering away.

The more room chats you have, the better. Like add_items, you can profitably share these between the relevant rooms. Having nine room chats shared in three rooms is better than each having three unique room chats. Too few room chats tends to give a 'groundhog day' feel to your area. A good way to make for really dynamic areas is to have chats that are, at least in part, randomly generated. We'll look at how to do that in the intermediate LPC documentation to follow, although the more inventive creator should be able to work it out from the material we covered in the chapters on introductory LPC.

References
You shouldn't rely on your own withered cortex for writing make use of the tools you have available. Buy yourself a good dictionary and a good thesaurus, or make use of the many free online versions. When you're struggling for a synonym, turn to your thesaurus. When you want to make sure that you've used a word exactly right, then check your dictionary. Don't leave it unresolved, or guess. 229

Epitaph Online

http://drakkos.co.uk

Be careful though with synonyms derived directly from a thesaurus they're fine to prompt you with words you already knew but didn't immediately remember, but synonyms are often loose associations at best. Don't turn over your own internal editing process to an automated solution.

Quality Assurance
So, having written a description, how do you know if it's okay? The easiest way to do this, and I am not making this up, is to read it out loud. Out loud, mind not just read it over. When you read a description out loud, you instantly pick up on clumsy phrasing, repetition, and scansion problems. A good description flows, rather than stutters. To show this in action, read the following description out loud:
There are trees all around. The trees are brown and look like trees normally look. Each of the trees have leaves. The leaves are green and brown and found around. All of the trees is unhealthy and looks as if they are almost dead.

This is an exaggeratedly bad description for instructional effect, but you should have instantly picked up on some of its flaws: Inconsistent scansion. Repetition of words Incorrect form of verbs

Scansion is perhaps the most difficult of these concepts to define for those unfamiliar with the idea, but it's to do with the natural rhythm that accompanies verbal communication. It's a term most often applied to poetry, but applies equally well to narrative flow. Syllables in English are either long or short, and the combination of syllables is what defines the meter' of a written piece of work. We all instinctively have a greater or lesser feel for scansion, but when something sounds clumsy' it's usually because the rhythm doesn't fit. That's something that just doesn't come across when you read something quietly to yourself it only comes out when you speak out loud. A formal discussion of scansion is way, way outside the scope of this material it's just something that you should bear in mind when putting together your descriptions. Once you've written the description to your liking, the next step is to put it aside for a few days and come back to it. Read it aloud once more, and then change it so that any new issues you've identified are resolved. Sometimes you get too close to something you've just written and you need a little perspective that only comes with time. If you can, get someone you know and trust to give you their honest feedback on what you've written - an outside perspective is even better than your own time-delayed thoughts.

230

Epitaph Online

http://drakkos.co.uk

Conclusion
Writing descriptions is a skill that comes with time and practice, but there are certain things you can do and think about to get yourself into the habit. Not every description is going to be a winner, and even descriptions that you like aren't going to appeal to everyone. That's fine though, the world would be very dull if we were all the same. All that matters is that you do the best you can to produce your highest quality of writing. It may seem like an insurmountable task to describe an entire village, down to every item in every room, but it soon becomes a reflex. It's also thoroughly enjoyable, when you get into the groove.

231

Epitaph Online

http://drakkos.co.uk

Non Player Characters


Introduction
Writing descriptions for NPCs introduces a new set of difficulties people really aren't all that different, when it comes down to it. While the scenic panorama visible from one mountain top will differ dramatically from the panorama available at another, it's usual that one person looks much the same as another, within some fairly well defined parameters. How then do you write descriptions for NPCs to ensure that they look unique and interesting? Additionally, NPCs are supposed to be responsive they are supposed to say things and react to things. That's an additional difficulty in setting them up well we have to write dialog. If you've never done anything like that before, it's a brand new challenge to meet. Finally, sometimes we don't want our NPCs to be killed because they are a part of in-game systems. We need to consider what kind of protections we can put in place for such NPCs, and why we might want to ensure they are protected in the first place.

NPC Classifications
As an informal system, we tend to categorise NPCs into one of several categories: Cannon fodder Service Quest Flavour Boss

Let's go over each of these categories in turn, and discuss what kind of creatures belong to each. Cannon Fodder NPCs Cannon fodder NPCs are those that you see wandering the streets. You can think of them as walking bags of tasty experience and loot. We tend not to spend a lot of time worrying that they are fully interactive, because that's not the role they fulfill in our game. It's nice if they respond to nods and waves and such, but it's only a small subset of players who will even consider interacting with cannon fodder, and an even smaller subset that will care that you made the effort. As long as your cannon fodder NPCs are described with a long description, kitted 232

Epitaph Online

http://drakkos.co.uk

out with suitable equipment, and contain some load_chats, you can move on to the next thing you have to develop. Remember, your time as a developer is limited, and you should focus your efforts where they are most needed. In many cases, you can get by by having them use one of the standard, pre-set personalities to populate them with chats and responses. Service NPCs Service NPCs are those that exist to facilitate in-game transactions. They could be Faction NPCs, shopkeepers, or soldiers guarding certain parts of the city. A player killing these NPCs will impact on the provision of that service. One solution to this is to simply not have NPCs as part of the player transaction - for example, we can create shops without shopkeepers with no trouble. That's cheating, though we want our game to be richer, not more artificial. What we can do is provide disincentives for players who attempt to kill them. That is the basic motivation behind practically every punitive system we have in the game to make it less appealing for people to murder NPCs of which that other people may need to make use. We make it so faction NPCs reward negative faction standing when they are killed, which provides a disincentive that can be quite powerful. In terms of writing service NPCs, descriptions and chats are important, but even more important is the system you put in place to discourage their murder. There are ways in which you can simply make them impossible to kill, but that's unrealistic and lazy. We want the game to allow people to be bastards if they want to be, but we put in place punishments for that behaviour so that there is a way of balancing player freedom of choice with protection for game systems. We'll talk about mechanisms to discourage gittishness later. Quest NPCs Quest NPCs are similar to service NPCs in that they are part of an in-game transaction, but their presence isn't continually required. Often they disappear when a quest has been completed, or are generated only when certain conditions are met. As such, we can't ensure their presence at all times, or even really necessarily tie them into a crime system. If a quest NPC is an animal, or a criminal, or in some way beneath the notice of some appropriate faction, it is not realistic for the faction to extend protection to them. Where possible, the systems that protect service NPCs can be extended to quest NPCs, but not always. Our solution to this in the past has been to make quest NPCs worth None XP, but while that discourages indifferent slaughter it doesn't do anything to punish someone who gets their kicks out of inconveniencing other people. Alas, we can't have everything. For Quest NPCs, interaction is king they should be responsive to questions and souls, as well as more chatty than other types of creature. Ideally they will mention things that are obviously related to their quest. NPCs hinting at the quests in which they are involved provide a useful mechanism for hinting that a 233

Epitaph Online

http://drakkos.co.uk

player should spend more time conversing with this NPC than they would with another. Flavour NPCs Flavour NPCs have no formal standing in the game, but are unique and interesting regardless. Most of the survivors in Dunglen fall into this category. The different kinds of cats and dogs are all examples of flavor we could have one kind of dog, but we get more by having lots of different breeds. If NPCs exist to provide flavour, they should be fully described and interactive to ensure that flavour is... uh... delicious. The danger is that people will assume they are part of a quest, so unless you are especially vindictive don't make them say anything that could lead players to that conclusion. Oh, where is my ring?' is an example of a chat that implies a quest is present, and it will send players off on a fruitless endeavour to solve it. The only result of this is that the players become frustrated and disillusioned. The questsense command helps with that to a degree (people can check to see if their hunches about the existence of quests are correct), but not everyone will rely on that as a system. Boss NPCs Boss NPCs fill the role of exciting combat opportunities', and are to be found usually as unique NPCs in the depths of an area. Their appeal comes from the fact they are a chance for high level players, or groups of players, to test their skills against a dangerous, unique foe. More than anything else, Boss NPCs should offer interesting combat experiences they should make full use of an appropriate range of skills and commands, and have a range of fun and hopefully humorous attack chats. They should kick, bite, disarm, crush, stab, knock people down, run away the whole range of activities needed to allow them to put up a decent fight. Their descriptions are not quite so important because, presumably, people won't get a lot of chance to read them. That's not to say you should phone it in', but most of your effort should be spent on making the combat interesting rather than weaving clever functionality into the description and chats. We have a system for allowing this through our AI behavior system we'll see that in a later chapter of the book.

NPC Descriptions
First of all, we need a clear picture of this NPC in our mind. While we don't usually provide detailed histories of our non-player characters, it's worth spending a bit of time thinking about what kind of life the NPC is likely to have had. If they are a military type, what are the likely results of their past campaigns to have been? If they have a specific occupation, what are the physical traits that are likely to be 234

Epitaph Online

http://drakkos.co.uk

associated with that occupation? People generally grow into the form they need for the life they have, so let that guide your mental image. Additionally, think about what kind of upbringing are they likely to have had. Aristocrats will bring with them a haughty bearing and a particular way of behaving to people. NPCs from Forn Lands will have characterizing features that can be brought out in the description. There are all sorts of connections you can make between the NPC's background and its observable behaviour. However, identifying characteristics are usually highly individual, and not generic. Scars and broken bones add flavour to a description, but if used too freely they seem awkward and contrived. Remember that a player is often going to see many dozens of your more generic NPCs wandering around, and it'll look weird if every one of them has a scar above his or her eye, or if every one has the same broken bone that has been 'clumsily reset'. One good way to add some variety is to use the basic structure as a template, and have the exact details generated randomly. For example, your NPC could get a random adjective such as Strong', or Thoughtful', or Healthy', and their long description could change subtly depending on which of these adjectives were chosen. Additionally, you can slightly modify skills and statistics based on the adjective, so that characters with the adjective strong' have a couple of extra points of strength, whereas one that is healthy' has an extra couple of points of constitution. It doesn't make a huge difference when fighting them, but it does add a little spice. From the same basic codebase you could generate the following descriptions:
The thoughtful soldier is a tall, thin man with a scar across his left eye.

Versus:
The strong soldier is a short, heavyset woman with a nose that has obviously been broken and reset many times.

We'll talk about how to actually achieve this goal in Intermediate LPC, but as with having randomized room chats it's something you could already do if you were willing to be a little bit inventive. You'll find many examples of this kind of thing in the 'live NPCs' in the game if you want to read a little bit of example code. Your unique NPCs have the advantage that they can be fully described without having to resort to generality. You can make specific references to exact features, size, shape whatever you like. Unique NPCs are usually named, and you can be a lot more specific in what their history is and how that has affected their appearance.

Equipment
The clothing that an NPC is wearing shouldn't be mentioned in the long description. If an NPC is supposed to be wearing something, give them that item 235

Epitaph Online

http://drakkos.co.uk

to wear, but don't mention it otherwise. There are three reasons for this: The inventory system of the MUD is standard and people expect to see items displayed in a particular way on an NPC. If clothing is mentioned, players should be able to look at the clothing. We have no equivalent of add_item for NPCs. You can't guarantee the clothes will be there they may break, or be stolen.

The choice of clothing you give your NPC is important as with long descriptions, it can either be something that adds to your NPC or detracts from it. If every instance of a generic NPC has exactly the same items, it's boring and jarring to your suspension of disbelief. We have a clother system for randomly dressing NPCs according to their affluence, so that not only is every NPC wearing a fairly unique ensemble, it's an ensemble that they could realistically afford to wear, or an ensemble that they could reasonably have been expected to scavenge from the city. For unique NPCs, equipment should be fit for purpose. Make sure the Three Key Zones are covered legs, chest and feet. Anything else you provide will enhance the NPC, but those three should always be provided. If an NPC has no shoes, it's barefoot. If it has nothing on its legs, it's naked from the waist down, and if it has nothing on its chest it is naked from the waist up. Those kind of things get noticed... In terms of weapons, bear in mind the environment in which the NPC will be functioning. This is certainly something to consider in Dunglen, which is a Scottish city and as such not as knee-deep in firearms as Los Angeles. While people are likely to walk around with their weapons out (because, you know, the world has ended), those weapons are likely to be improvised for the main part. You can certainly throw the odd automatic rifle in there, but remember this is a world where new ammunition is, on the whole, not being made. One neat touch that adds a great deal of immersion to the game is if an NPC has random, useless items that hint that the NPC you just murdered has a spouse and children somewhere. You could very occasionally have an NPC carrying a small present containing a wooden duck, with the label Happy Fifth Birthday, Sue!'. It may not even be noticed by most players, but it'll be a nice and unexpected laugh for the ones paying attention.

Dialog
For NPCs where responsiveness is important, it comes down to dialog. Dialog is really quite difficult to do properly because at its heart it is a tension between the convenience of gameplay and the believability of the revelation. It's tremendously common in games for NPCs to reveal every intimate detail of their life to the most casual of inquiries, but that doesn't make for a rich dialog. It may be appropriate for a passive medium such as a book, but it doesn't work in an active environment like a game. 236

Epitaph Online

http://drakkos.co.uk

With dialog, we are looking to build a genuine opportunity for players to interact with our game world. If our characters are to be believable, they need motivations. They need back story, and they need reasons to talk to the player. This is mostly an issue with quest and flavour NPCs rather than the others, but a believable character is one that requires us to tease out the information we need rather than to have it dumped entirely on us as a lengthy monologue. Dialog in a MUD is closer to that of a film than that of a book dialog in films is snappy, direct and delivered as exchanges. It's hardly ever paragraphs of unsolicited revelation. So, our first aim in writing dialog is to build up a tree of how revelations will unfold. Our character may be looking around for something, and that hints to the player that a quest may be somewhere in the offing. How our NPC responds to enquiries is how we build the dialog. This is dull:
> ask beefy about lost things You ask Captain Beefy a question about lost things. Captain Beefy says: Yes! I have lost my marbles! I lost them down by the Old Creek yesterday. Could you try to find them for me? I will give you ONE MILLION DOLLARS!

Where's the interaction here? Where's the drama? You're just typing a thing and getting candy. People don't talk like that. It's much more interesting, from a game design perspective, if you make the revelation a part of the interaction:
> you ask Captain Beefy a question about lost things Captain Beefy says: Hrm? Oh, yes. I lost my marbles. Careless, really. > you ask Captain Beefy a question about where Captain Beefy says: Oh... I suspect it was down by the Old Creek yesterday. > you ask Captain Beefy a question about finding Captain Beefy says: If you can find them, I will reward you. > you ask Captain Beefy a question about reward Captain Beefy says: Oh, how does ONE MILLION DOLLARS sound?

If you want to build a convincing discussion, you can even build in suspicion you can make it so that you need to talk the NPC into giving up the goods. Why would you, some stranger in a world gone to hell, be willing to help someone else without any obvious gain? When writing these dialogues, bear in mind your mental picture of the NPC again. How are they likely to speak? It's very easy to lapse into stereotypes here, so try not to. Are they direct and to the point? Flowery and poetic? Do they speak clearly? Mumble? Lose their train of thought? Are they articulate? Do they communicate mainly in grunts? All of these things help you frame the dialog appropriately.

237

Epitaph Online

http://drakkos.co.uk

Conclusion
This chapter hasn't really been about the writing that goes into NPCs it's more about the thinking that lets you write them. Having a firm idea of why your NPCs exist, what they are for, and how they are supposed to interact is a great way of making sure that you're emphasizing the right things in the right areas. In the next chapter we'll talk specifically about Zombieville and look at the NPCs we're going to make available, what they are for, and what dialog opportunities they are going to present.

238

Epitaph Online

http://drakkos.co.uk

Beasts of Zombieville
Introduction
We now return to our regularly scheduled programming first we talk about a Thing and then we relate that discussion to our design of Zombieville. In this chapter we're going to look at the NPCs we are going to make available in our development, including what they're for, what we need to provide for them, and how our players will be able to interact with them. We have a fairly small development, so we can't go overboard with this we may have cause to add more if we need to increase the feature density of the village, but at the moment we're really looking at three: Our undead headmistress A survivor in the knitwear shop Some zombies to kill

Only the first of these are required as part of the development, but having a survivor gives us a way to provide game clues, game story, and an obvious way of setting up a quest hub. And if we have a survivor hiding in a knitwear shop, we require peril and that is where our zombies come in!

Our NPC Manifesto


The headmistress is a boss NPC, which is an exciting kind of NPC to talk about our boss NPC system allows us to set up unique powers, unique loot, and all sorts of neat features. We'll talk more about this later in this chapter. Our survivor is a service NPC he or she will be the quest hub of the area, and will direct the player to the school with tasks such as 'Oh, I am the librarian of the school, and I have been going mad thinking of what has happened to my library'. As the player completes the quest, they can hand them in at the survivor and get the next in the chain. Finally, we have our cannon fodder NPCs the zombies. We have lots of different kinds of zombies in the game, but we'll just have these being the standard, normal shamblers perhaps with a little ornamentation, but there's no need for us to spend a lot of time coming up with interesting combat mechanics for the that's what our headmistress is for. Together, this creates the NPC complement for our village it's not much, but then it's a small village and isn't likely to attract much in the way of activity. Our task for each of these is to spec out the details although we can write the descriptions in advance of actually writing the code, we're not going to do that. Instead we're going to develop the framework into which each is to fit. 239

Epitaph Online

http://drakkos.co.uk

The Zombies
Let's start off with the simplest NPCs our cannon fodder. We're going to have three or four of these wandering around a time, and so our discussion about randomizing some of the setup becomes relevant. Zombies already get randomized descriptions, since their various horrible injuries are all appended onto the description we get:
This child couldn't have been more than about three or four when it turned. Its tiny body is grey and hardened by the lack of life fluids. Lack of muscle development makes it a greatly reduced threat, but don't be fooled - any of these things can be deadly. The right arm of the zombie toddler has been completely lost leaving tendons to hang limply from the shoulder socket like dry, decaying noodles.

More is always better in this case though in addition to the grisly wounds, we can randomize the first part to reflect the demographic makeup of the city. We can have old people, young people, fat people, thin people it won't change the way that they work in combat or their skill levels at least, not dramatically. Ideally they'll even have different short descriptions to add a bit of interest. In Dunglen as an example you'll find cadgers, mendicants and beggars they're all the same NPC, just with different shorts. We can make all of that Come True with only a tiny little bit of Code-Fu. To begin with, we'll provide a list of all the possible short descriptions:
({"spinstress", "schoolchild", "farmer"})

Next, let's work up some distinguishing features for their hair:


({"mohawk", "lank hair", " cropped hair"})

This is just the first part of her description, but already we have a mechanism for distinguishing one zombie from another zombie. The more of these we have, the more outcomes there are. Let's add in weight and height:
({"fat", "thin", "skeletal", "plump", "well-built"}) ({"tall", "short"})

And hair colour:


({"blonde", "brunette", "red", "black"})

And then we combine them together into a standard framework, such as :


This is the shambling corpse of one of the village's $type$. $pronoun$ is a $height$, $body_type$ person with $colour$ $hair$.

By slotting random elements of our lists into this, we get things like: This is the shambling corpse of one of the village's spinstresses. She is a 240

Epitaph Online

http://drakkos.co.uk

tall, plump person with blonde cropped hair. This is the shambling corpse of one of the village's farmers. He is a short, well-built person with a blonde faux mohawk.

The more entries we add to the list, the more variation there can be. It's a great way to get extra Value from very little expended effort. We will have to be careful when we code it so we cal allow for 'a blonde' and 'blonde', but that's okay we can do that. In terms of their outfits, those are going to be related to what kind of NPC they are. We'll dress farmers differently from schoolchildren, and both will likewise be dressed differently from spinstresses. They won't be dressed in the usual random ensemble of a survivor, because they are the reason people scavenge, not scavengers themselves. Their load chats are going to be variations on the theme of moaning and groaning. Not very interesting, but it would be a little strange if they were incredibly articulate while also being zombies. In terms of their weapons, they won't have any. They're zombies, and even if they were carrying weapons before they were infected, they won't be carrying them any more. Interaction possibilities revolve around well, eating brains. They'll be auto aggressive and will attack any player they see. That means we don't need to worry very much about add_response. We can save that analytical effort for when we develop our survivor, who will need to be fully responsive.

The Survivor
Our survivor (let's make it a woman), is the only real conversationalist in the entire village we can spin a narrative around her in any way we like, but for the sake of progressing let's say she's the former librarian of the school. When the infection struck the village, she found the only place she could safely barricade was the knitwear shop, where she now lives she sneaks out in the evening to scavenge for supplies, but she has been on her own for a long time and as a result has gone a little crazy. Her main worry in life is not the zombies, or where her next meal is coming from instead, it is the library she left behind, and her old friend the headmistress. She can't get into the school herself, but she would be very grateful to any player who could manage that feat. Since our survivor is a quest NPC, it is hugely important that she is fully responsive, and that she gives the necessary context for players to know what it is she requires of them. She'll probably be dressed mostly in tartan knitwear the shop will have provided her clothing requirements, but aside from what was in the flat over the shop she won't have had much in the way of food or drink. We'll need to make sure that 241

Epitaph Online

http://drakkos.co.uk

her responses and chats explain her current situation. We can describe this survivor without resorting to randomization she's a unique NPC. As such, we should also come up with a name for her so that she's not just 'a survivor'. Something like 'Agnes McKay' would be suitably Scottish for our requirements, but anything that is thematic would do. Her chats will centre around the school, the library, and the children that she abandoned when it all got so bad. She might have stories to tell about what happened perhaps she lef the school because the headmistress had gone crazy and began to canablise the children, or perhaps she simply hadn't gone into work that day. Perhaps she escaped when it was obvious the infection was spreading. It's our choice as to why she is where she is. She might say things like: I will never forgive myself for what I let happen to those children. Oh, the books I can't believe I let that happen to the books. I swore an oath as a librarian, and yet look what I let happen to my library. Oh, my dear friend I am praying for your soul.

If she was a direct observant of the scenario in the school, she may be somewhat hesitant to explain what happened, especially if her friend was the one who turned on the children. Her responses would then be halting, and ambigious.
> ask agnes a question about school Agnes McKay says: Oh, it is so terrible, so terrible. about it. I try not to think

> ask agnes a question about terrible Agnes McKay says: Those poor children, but... it was the kindest thing, perhaps. > ask agnes a question about kindest Agnes McKay says: There was no food, no food at all - she, she did what she had to do. But I couldn't. > ask agnes a question about had to do Agnes McKay says: They were all starving, she had to do something - something until help came along. > ask agnes a question about something Agnes McKay says: Some of them were already on the verge - their passing at least eased the suffering of the others for a little while.

Her dialog should also lead players to realize there is a quest in all of this for them. To make it a little more believable, we can hint that her obsession about the library is simply masking a much darker, much more painful obsession.
> ask agnes a question about library Agnes McKay says: I can't sleep at night, thinking about the state the library must be in! > ask agnes a question about state Agnes McKay says: Oh, I can't go there myself. grateful if someone could tidy it up for me. > ask agnes a question about tidy But I would be ever so

242

Epitaph Online

http://drakkos.co.uk Those

Agnes McKay says: It's silly, I know. But it does occupy my mind so. poor books... they didn't stand a chance. > ask agnes a question about books Agnes McKay whispers: They were so young. that.

It shouldn't have happened like

We won't put anything in place to stop people killing her because our world is not a nice world and there's no real way we can justify it.

The Headmistress
Our boss NPC opens up a whole range of options that we don't have with other NPCs specifically, she will have boss powers and boss loot. She won't be especially responsive, because well, zombies are not. But we can give her some relatively evocative chats. Perhaps, because she is a unique zombie, she has retained some semblance of speech that's not consistent with the other zombies in the game, but that's okay boss NPCs are unique by their very nature, and it is game canon that the infection affects different people in different ways (thus, the variety of zombies that we have available) The headmistress also, technically, doesn't really need to be clothed her clothing could easily have rotted away. However, the thought of the rotting undead genitals of some sixty year old woman doesn't do a lot for me, so let's make sure she is we'll clothe her in some suitable teaching attire. She also doesn't necessarily need to be described to the last detail certainly the imagination of the average player is more than up to the task of filling in the blanks if we provide them with an intentionally fuzzy overview. Much as how the best horror movies and books leave the details to the imagination, there is a lot to be said about maintaining the mystery for our headmistress. It's perfectly okay to talk around' a description without giving away the farm. On the other hand, if we have suitably horrific imagery in mind, we can use that too:
The infection has not been kind to this poor woman - her body has erupted in violent, pus-filled boils and blisters that eject greasy fluid like minature volcanos. Her fingers are like iron claws, hooked and sharpened to impossible points at the nails.

On the other hand, we can just hint at the horror and let our players furnish the mental image themselves:
That the headmistress was once woman would never be apparent from her appearance. The infection that worked through her has shed away every last trace of humanity, leaving only a core of pure rage and oozing sores.

In the former, we describe her appearance that's fine if you feel you can do a good job with describing something that exists entirely in your imagination. In the second, we describe the essence, not the specifics we let the player decide exactly how the headmistress appears in their own mind. I'm not saying this is the best way to do it, just that it's an option when dealing with things that are entirely 243

Epitaph Online

http://drakkos.co.uk

alien to our life experiences. Since we want her to be an interesting character, we can take the opportunity to fill her with some actual chats in Real Human Language. At one end of this, we can have her being entirely articulate: They all had it coming! They were delicious! Num num num get in ma belly

But while we can exercise some bending of the rules with boss NPCs, it just doesn't ring true. It's far more likely that she if she can vocalize at all it is only going to be fractional and broken up with growls and groans and moans. We can however use this base chats, and then manipulate them to interject growls and whines randomly in the word. That way, each time she'll say something slightly different but eventually you'll be able to work out what she's saying. That sounds pretty neat, so let's make that one of our goals.

Boss Battles
Having decided what our boss NPC looks like, we need to consider what it is that makes her such a challenging foe. We are not limited here to the set of abilities that players and other NPCs have every boss can have entirely unique powers that do entirely unique things, and it is that flexibility that allows us to create interesting boss encounters. Boss NPCs are not simply larger, more powerful zombies they are different creatures entirely that require specific strategies to defeat. We shouldn't stuff too many of these powers into a boss NPC, or the fight becomes impossible to strategise we need enough so that people need to react to a range of situations, but also enough so that the fight is actually manageable. Three or four powers seem like a reasonable number. What we also need to consider is the interaction of those powers. Every boss should have a strategy that can best it, but that strategy shouldn't be trivial. A boss that does massive amounts of melee damage is rendered trivial by a group that only uses ranged attacks. Likewise, a boss that is all ranged damage is messed up by an all melee group. Ideally, you want your powers within a boss to ensure an interesting set of circumstances. Forcing players to change their positions mid battle, lest something terrible happen, is a good way to mix things up. You can have boss powers that change the position of players for you you could have (as an offbeat example) a magnetic zombie that every so often causes everyone with any metal at all on their person to be moved into melee range. You could have a zombie with a power that causes ranged ammo in someone's inventory to explode, meaning that ranged combat becomes risky and indeed if the risk is sufficiently high people may need to strictly ration their ammunition. 244

Epitaph Online

http://drakkos.co.uk

When designing a boss, think of the following situations first: How does the boss deal with an all melee group How does the boss deal with an all ranged group

You need to deal with these two situations mixed groups are, on the whole, what you are looking to encourage because that requires a blend of skills. The simplest way to manage this is to have your boss with some melee special powers and some ranged special powers. A good third power is one that requires people to pay attention so that they can react appropriately a boss that occasionally gives off a blast of disgusting, poisonous gas might require everyone in melee range at the time to disengage. Another boss might screech and run violently in a circle striking at everyone in range. Perhaps the boss does a mental calculation of everyone's location and offloads a series of chillingly precise bullets at where everyone was when he checked that would require *everyone* to switch position until he was done. If you can set up interactions between powers too, that creates an interesting mechanic. Perhaps your NPC will pick up someone in melee range and throw them at someone else, rendering them prone. If you combine that with some kind of 'trample' command whereby the boss charges at someone prone at range, then you create a situation whereby everyone must pay attention, and compensate for the fight as it goes. In a fight like that, it becomes 'Quick, get everyone up on their feet!' We're not looking to create an actual in game fight here, just explore the concepts so let's not worry about being especially creative here. We'll have one melee special power, one ranged special power, and a power that forces players to run out of melee range or be horribly punished. Let's have our headmistress still clutching, in her right hand, the steel ruler she once used, decades ago, to punish naughty children. Throwing that ruler at someone in ranged combat seems like a reasonably good boss power. We'll make her do this only if she's not in melee combat, which creates a need for the boss to have at least one person in melee range. We've made mention of her pus-filled boils, so let's have another power that causes one of them to erupt and cover everyone in melee range. The last power will be that she goes berserk and does quadruple damage to anyone drenched in her pus. That creates a situation whereby melee warriors need to keep an eye on the boss or risk being killed very quickly indeed. Her third power will be based on her steely clawed fingers, and she will use them to snatch a weapon from someone she's not currently focusing on in melee range. She'll then throw this weapon at someone in range. That's an interesting suite of powers she is a boss that has most pull in melee combat, which means that players will have most of their luck trying to engage her, but if you stay out of her melee range her metal ruler comes into play. Also, if you are the only person in melee combat with her (if you are tanking her, in other words), you're most likely going to have your weapon snatched off of you 245

Epitaph Online

http://drakkos.co.uk

which will undoubtedly cause problems. On the other hand, if you all engage her in melee combat it's almost guaranteed that she'll spray her pus over you and you'll take hugely increased damage while she is berserk. The more people who are in combat, the less chance there is that everyone will react in time. What makes this an appropriate boss encounter is that there are counter strategies all ready for players if you are the solo melee tank, then you either want the skills to be able to avoid that disarming special, or you want to be an unarmed tank which negates the problem. Player skill is encouraged by ensuring that there are timing imperatives in the battle you need to get out of melee range when she's about to erupt, but if you stay out of melee range too long one of the people in range is going to take a beating from that ruler. When developing an interesting boss encounter, the best thing to do is let people try it out. Often they will come up with strategies you certainly did not envisage, and the fight is either rendered trivial, or much more interesting, as a result of that insight.

Conclusion
Really, our cannon fodder NPCs are not very exciting they're there because they have to be to justify the situation and also to add the peril we like to place our players within. Most of our development effort will be expended, sensibly, onto our boss NPC and our quest hub survivor. Both of these NPCs will allow us to explore a considerable amount about how Epitaph manages some of the complexity of developing interesting content, which is largely the reason that we (and by 'we', I mean 'I') have taken the design decisions we have. As with anything else in this particular book, there were a dozen other directions we could have gone, and interested creators should definitely spend time brainstorming what else could have been done here. Really, we just needed to pick one, and this is ours.

246

Epitaph Online

http://drakkos.co.uk

Feature Development
Introduction
We've now spoken about our quests, our rooms, and our NPCs. The next step is to look at the two features that mark out our development the school, and the shop. The library in particular is a unique feature that is not supported in any of the MUD base inheritables, and so we're going to have to think very carefully about how it is to be implemented. It's also a good idea to think about the shop before we put it in place, to make sure we can actually have it produce a range of interesting and unique merchandise. If we can't do that, we can rethink our plan as necessary. Plans are malleable after all, and we put all this thinking in before we start coding so that we can completely change what we're aiming for with no cost.

A Local Shop For Local People


Let's start with the easy one first the shop. Producing items may not be the most inspiring of tasks (although they add a huge amount of richness to the game), but if we are adding a new shop it's a cheat if we don't make its stock at least partly original. Luckily, Roundworld is the best inspiration of all for items because whatever we may be developing, we will find examples on The Interwebs. Researching for inspiration is a big part of putting together a unique development you don't need to copy things exactly, but they can give you the starting point you need, and still ensure that your items are grounded in reality. For a shop to be worth its title as a feature', it should have a reasonable amount of unique stock the more the better, although if there is too much it may be sensible to spread it over multiple stores as to more accurately reflect the feature density of your area. It's better for two stores to have a reasonable amount of stock than for one shop to be oversupplied and another to be barren. It's also better for there to be two reasonably stocked shops over one that has a huge stock list that cannot be easily navigated. I won't give specific URLs for inspiration here, I'll just recommend you fire up Google and look around for a bit. Looking for 'tartan knitwear' outfits will fill your browser with possibilities, and most are even safe for viewing in work. Others aren't, and if you find any of these you should send them to me so that I can... uh... properly protect you from them. Yes. Your first task is to decide on exactly what the remit of the shop is is it purely for knitwear? Maybe it's also doing a side trade in Scottish staples like canned haggis and shortbread. Just because we decided on a type of inventory at the start doesn't mean we can't refine it as we go along. 247

Epitaph Online

http://drakkos.co.uk

Once you have decided on what is to be stocked, think about what is going to be available. You should aim for a scavenge list of roughly eight to ten items, which doesn't give you an awful lot to work with if your remit is too large. Four categories of items will give you around two of each that doesn't permit a wide range of possibilities, but it would be possible to buy an outfit' rather than having to mix and match. Let's make it easy for ourselves and have our shop selling two kinds of items hats and skirts. That means we need to provide four or five examples of each that's a sufficient range to provide interest, and yet not so ambitious that we have little hope of hitting the target. It's a nice balance between the two. We'll also add a couple of schematics here too, because those are always worthwhile for players to find, even if they are just for a certain kind of knitted hat.

The Library
Now we come to the Meat and Taters of the development our school. While we already know this is a questing hub, we also need to make it interesting on its own behalf since that was its original intention. It doesn't need to be bizarrely elaborate, it just needs to add to the richness of the game. We already know of the 'sorting the library' quest, but once it has been sorted it would be nice if people could make use of it to find out information this information could be about Zombieville, about the Headmistress, about Scotland, or... even better... about other players. However, we don't want to have to write hundreds of books, even fake books of a paragraph each. It's Much Too Much to do anything like that. What would be best is if we had some kind of index card system listing topics, and allow the player to simply research' the topics on that list. Doing so would give a random piece of information about the topic at hand. For example, we could have a topic about the school, and provide a range of snippets to the player who chooses to research that topic: You research the school in the library, and find out that it was where a certain pre-infection rock star first learned how to play guitar. How interesting! You research the headmistress in the library. You find her in an edition of Who's Who, which indicates that she was actual minor royalty. The more you know, eh?

Each of the snippets can lead on to other snippets, making the library a little puzzle of its own. Indeed, it could even be a quest to piece together some great mystery, but making such a quest dynamic, randomly generated and yet still possible to solve is a tremendous challenge and one that we will not set for ourselves for the time being. The real meat of such a feature would be a way to research players, because that adds a measure of interest to socialisers as well as explorers. Obviously we don't 248

Epitaph Online

http://drakkos.co.uk

want the information given to be overly powerful (no revealing of skills, or stats, or level), or freely available elsewhere (projects, plans, refers, etc). We're not limited to fact though, there's nothing to stop us making up slanderous lies about players provided we do it in a humorous enough way. First though, we don't want every player to appear in the library having your name mentioned should be a mark of honour, not just a random occurrence. It's unlikely that a brand new player with no renown would appear in anyone's library. Ideally it would be possible for someone to be entered into the library as a reward for some other achievement. Perhaps those who managed to ascend to see the Headmistress get their name recorded in the library for others to research. In that way, it becomes another way in which a player can make an impact on the game world. If we do it that way, we shouldn't have their information random, because it would be possible for someone to simply research the same person over and over again to find out the full range of possibilities. Instead, their information should be generated randomly when the necessary steps have been achieved, and that same information returned each time that person is researched. The information should ideally be Funny, because that is an incentive in itself for people to research other players. And it should be obvious where it comes from. Maybe once the Headmistress has been defeated, the librarian pops in and makes a note on in a notebook:
You research Drakkos in the library, and find a neatly handwritten report in one of the librarian's notebooks. I met Drakkos today. He's an odd little man, and smelled vaguely of donkeys. He grunted something unintelligible, turned around, and promptly fell down the stairs. I hope he comes back some day'

As with the randomly generated long descriptions of our zombies, we can adopt a similar system for the library. First we add some descriptions:
({"odd little", "small-minded", "confused", "smelly"});

We can add the gender to that when the information is entered to make it correct. We then add a little insult:
({"smelled vaguely of donkeys", "walked with a stutter", "talked with a squint"})

Then in a similar vein, we setup the action the player took, and the result. We then end with a little afterthought. We could even have several of these templates into which we slot the information after a short time a single template becomes so familiar you can see the Oz behind the curtain', and by mixing it up a bit you can postpone that moment so that it all appears Magical for longer. We should also consider our library as an opportunity for some useful taskmaster increases any feature like this is going to involve some measure of skill in 249

Epitaph Online

http://drakkos.co.uk

teasing out useful answers. It doesn't have to be anything especially useful something in the mental tree is a good possibility. It's unlikely taskmaster chances in these skills will draw in the Punters, but it's a nice bonus for those who want to explore the little treats we have in store for them. To further tie the library into our plans, we could liberally sprinkle a few quest hints here and there. They don't even have to relate to this village, but if you are going to hint at quests elsewhere make sure you clear it with the appropriate authorities. In game quest hints are great because they are entirely in theme in a way that the web interface can never hope to be, and provide a cross-polination of developments that makes our game look much more coherent.

Feature Creep
Now we add a word of warning to our discussion of features the importance of restraint. There is a reason why we put a plan in place before we start coding it's to set the constraints of our project at something reasonable and achievable within a specific time frame. What tends to happen as you develop is that you think to yourself Oh, and I will do this! And this! And wouldn't it be cool if I did that too?' This is common, and entirely understandable. The problem is this you will always have more ideas than you will have time, and if you continually get distracted by new ideas, you will never get around to finishing your project. There's a phrase for this feature creep. As a development progresses, more features start making their way into your todo list. It's incredibly important to stick to a solid plan. I don't mean you have to be constrained by it you can change things around as and when you need to. However, you shouldn't add things to it on a whim. Every addition should be on the basis of you fixing some kind of issue that was encountered during the development. There will be plenty of time when the project is completed for you to add new features. I talk again abot Discworld MUD because that has been in operation for nearly twenty years and as such is full of excellent examples of this problem. There are dozens of projects on Discworld that were delayed by years because of feature creep. The original Lancre Castle was started way back in 1997, and entered playtesting in 2008. During that time it expanded from a fairly reasonably achievable project to a vast, lumbering monolith all due to feature creep. As new creators came on board the project, they brought their own ideas and put forward their own plans for how the castle should be. I'm not saying that these ideas were bad, but they were expanding a development far, far beyond its original scope. Eleven years is an awfully long time for an area to be under development. The Milords and Miladies Wedding Pavilion was started in 1998, and took until 2004 before it went into the game. Long, prolonged developments are a death march for a domain they are indicative of a thing called the Concorde Effect, or the Sunk Cost Effect, whereby 250

Epitaph Online

http://drakkos.co.uk

the amount of effort invested in the past is used as a justification for future effort despite the fact that is an unsound judgement. They also proves Brook's Law adding manpower to a late software project makes it later . Throwing creators at a death-march project exacerbates burn-out problems. It's difficult to remain motivated when you're working on a development that has been ongoing for years and years, with no sign that you're going to see any of your code in the game any time soon. Best avoid this problem entirely by setting yourself realistic and achievable goals. New feature can be added into your development, but leave them until the end. Develop according to your plan and only once that plan has been achieved do you consider what else needs to be incorporated to make your development as good as it can be. There are a few examples of this in our plan for Zombieville the village is a little sparse, and could benefit from having another feature. There is scope for another quest or two in terms of the backstory we've set up. The problem is if we plan all that to begin with, we'll never get finished. We plan something we can do, and then at the end when we have a finished (albeit Spartan) development we can extend it. As a new creator, you're not going to know what your limits are. Unless you have a lot of experience with coding, you just aren't going to be able to estimate how long things are going to take you to write. Once you've added something to your plan, you are committing yourself to writing it especially if your plan has been approved by your domain administration. Many creators find it difficult to say Actually, no I'm not going to have that feature after all'. The trend is almost universally for features to be added, not subtracted. This brings us to the second important point you should be prepared to make cuts in your plan if you find you were originally over-ambitious. This is something you should discuss with your domain administration, but we prefer modest but completed projects over ambitious but never-ending developments. Features can always be added later. Exactly how long a development will take will vary from creator to creator I have no way of explaining how you will estimate it other than it comes with experience'. Ideally a project will be relatively short, giving you a reasonable turnaround from project to project. If there is a particular feature that is going to cause problems with your estimation, then consider recasting the feature. You can scale it back, or remove it entirely and replace it with something more achievable. This is not an admission of failure, but instead a mature and responsible attitude to meeting your creator obligations. You need to repeat this like a mantra it is not an admission of failure. The ideal approach then is to be conservative in your plans, finish what you say you will develop (and no more), and then later direct new features to plug gaps you have identified. This is the Path To Success, Enlightenment, and Love.

251

Epitaph Online

http://drakkos.co.uk

Conclusion
A good area has unique features to tempt players to explore. Our development will have two a clothing store full of unique and interesting tartan garments, and a library chock full of quests, narrative, and even a few jokes. It's common to look at these two features and think No, it's not enough' when we're developing In Anger. That kind of thinking leads to the danger of feature creep, and we should avoid that when possible. Think of this as a Law of Nature plan the plan, but keep it reasonable. Develop that plan as is, making adjustments only on the basis of what you can realistically achieve. Once your development plan has been completed, and the project has been coded, do a feature density analysis and decide what additional features are needed to make your project fit for the game. Only by being strict with this can you avoid turning your project into yet another Nightmare March for your domain.

252

Epitaph Online

http://drakkos.co.uk

Finishing Up
Introduction
We've pretty much planned the hell out of our little village, and we are coming to an end of this set of material. The last thing we need to talk about in terms of our specific development is its integration into the wider context of Epitaph. This may be easy, or confusingly difficult, and that will depend on how well defined your domain is in terms of geography and infrastructure. Adding a street to Dunglen is easier than adding a village in the outskirts of Scotland, for an example of what I mean by this. Dunglen has a structure we can add things to outside of the city, Scotland is undefined. This is largely something that will be a problem for your domain as a whole rather than a problem for you specifically, but we'll talk about the issues that are likely to be encountered so that you are prepared for the discussions to follow.

Integration Mechanisms
It is almost never a specific intention to make an area difficult to reach. It's usually a consequence of geographical remoteness. Perhaps the areas linking two other areas don't exist and so it doesn't make sense. Perhaps there is no obvious way in which a development could be reached (maybe it's on an island and there are no boats in the game). That kind of thing is more common than you might expect. While it's always possible to have artificial means of access to an area (you could have an NPC who will teleport you to a location if you pay him enough dog food), these are artificial and unsatisfying for the majority of locations. The ideal situation is that there is a placeholder already available for your development. You could be walking around Dunglen and see a new street, but not be able to enter it because it is 'blocked by rubble' or some such. That's a placeholder it's where your street will go when it's finished, and until it is it is blocked by rubble. For areas that are not covered by terrains, this integration has to be done in a different way. We either need to find some kind of relatively believable mechanic (perhaps a helicopter touches down at a certain time in a certain room every day, and if you get on it will take you to a different place). For those players without access to easy transportation though they are an exercise in frustration, turning active play into a Waiting Game - you wait for the helicopter (blink and you'll miss it), then you wait for the helicopter to arrive at the location in which you are interested. Along the way, you kill time.

253

Epitaph Online

http://drakkos.co.uk

Blending these approaches is always going to give the highest amount of satisfaction - the more accessible your area, the more likely people are to want to visit it, and that's in your best interest as a creator.

Advertisement
Having completed a development, it's only natural that you should want to tell people to go visit it. There are several ways to do this - the simplest way is to do it Out Of Theme, such as posting on the boards, making a note on the recent developments blog or, if the area is large enough, making a news announcement. There is nothing wrong with any of these approaches, but also consider making the news available in the game. Creators can, for example, seed rumours through the game, and these rumours become available when people search up appropriate objects. You could write a small newspaper article about the village of Zombieville, and when someone finds such a newspaper they will say 'Oh, hrm! That is new!' What you're looking to do is create a buzz, not necessarily signpost everything you're not a tour-guide. Your advertisements also shouldn't undermine the thrill of exploration that people get out of finding something new. This is an example of a bad advertisement:
Come visit the new area in the Support domain! It's called Zombieville, and you'll find it by going east, north, east, west, south, and west from the village of Deadville! It's a rural village. It has a school and some quests, and a shop with some things! You should check out the library especially, because it's full of Red Hot Peril!

The problem with this is that you've removed all of the mystery - those who don't find your advertisement convincing just won't visit, and those who like to be surprised will find nothing of interest left to explore. What's much better is if you hint at the great things to come for those who wish to spend a little time investigating. An example of survivor graffiti that might work:
Don't go to the village of Zombieville - they're all dead there. I tried to search the school, and only barely got out with my life. I don't know WHAT that fucking thing was.

People are going to assume that there are new quests when an area is introduced - those who don't assume will soon be made aware by the increase in available quest points. Saying it's a village is going to also provide some expectations - you don't have to remove the anticipation by telling people everything they should look out for. We also mentioned that there is danger in the school, and that will imply a boss to people who are paying attention. We are criticised occasionally by players (and other creators) for our often cryptic news posts, but I personally favour them as being much more in theme with our development sensibilities. While I don't endorse cryptic posts relating to gameplay issues (such as rebalancing of weapons, skills, removal of spells or such), I do feel that they enhance the experience for in-game developments. However, this does 254

Epitaph Online

http://drakkos.co.uk

not extend to the recent developments blog - posts there should be clear and unambiguous, otherwise it's not fulfilling its designed role. As with anything though, it has to be done properly. A news post saying 'There's a new thing' doesn't do anyone any favours. First, you need to give a fair idea of what the new thing is. You also need to give a hint of some kind as to where to find it - that can be built into the style of the writing. Imagine if you were advertising a makeshift city full of teenagers:
Lol, u shld come 2 our new city, its better than urs, rofl

(See, I may be old but don't pretend I don't know how to jam with the kids). The first hint is in the style of writing - 'Hrm, textspeak. That leads implicitly to a clue in where the development is situated - it's situated somewhere where there are a lot of children. It's a city too, so it's probably quite big. You'd be forgiven though for not making use of in-theme postings, since the most vocal minority of our players will complain bitterly. Still, we're allowed to have fun as well!

Bookkeeping
A new area always causes problems, no matter how long it is developed and how thoroughly it is tested. The more people exploring an area, the more people there are to do things that you wouldn't have dreamed of in a million years. Things that seemed like a good idea to code at 3am in the morning turn out to be really quite bizarre in the cold light of day. Everyone has different things that they think would be cool, or funny, or whatever, and it often differs wildly from person to person. Imagine that spread over hundreds of players. Everyone will have different issues:
I tried to chew the delicious chairs here, and I got a runtime.

Some of the problems will be technical in nature, but a lot will simply be because of the novelty of the area. As such, upon completing any development you should do what you can to minimise the burden on the liaisons who will be in the frontline fielding questions. The first step in this is to introduce your development with the Five Dubyas - this is a post on the Game board covering five pieces of information:

255

Epitaph Online

http://drakkos.co.uk

Dubya What Who When Why Where

Information to Give What is the new thing that has been added, or what is the old thing that has been changed? Who was responsible for the development (so liaisons know who to contact for resolution of queries, or to whom they should direct the undoubtedly lavish praise) When is the change scheduled to go live. Why was the new thing introduced, or why was the old thing changed? How do people get to it, if it's a new area?

This gives a quick, at a glance overview of some of the most important questions for others to be able to answer, but by itself its not enough detail. Sadly, we are all better at doing this in theory than we are in practise, but you should also thoroughly document each part of the system that is likely to cause questions. For example, people aren't going to ask much about the shop we've provided because shops are a common feature across the game. They will however have questions about our library, and the quests. Those questions will be directed, in the main, to those creators foolish enough to be visible, and correspondingly few of them will be experts on the code you just put in the game. The information you write for this is Creator Only, so you can feel free to include useful functions, exact solutions, difficulty levels - all of that information is useful for a creator trying to track down player problems. Ideally you should be doing this as you go along - if you are keeping to your plan, you'll already have a fair amount written up to draw from. Where you choose to make the information available is up to you - there are several systems in place of which you could choose to make use. We have a developer wiki which is an excellent place to store such documention, and we also have the Oracle system. Ideally, the information will be available in both, although in subtly different forms. As I say, we are all better at talking about this than doing it, but please make an effort to provide this information. It will make life so much easier for everyone you won't have to keep answering the same questions for different liaisons, liaisons won't need to keep hassling you for information, and anyone caught in the crossfire (such as a lord without the good sense to be invisible) will be able to look up the answer to any queries without needing to really pay attention. Everyone wins! In terms of the information you provide for players, you should also consider what hints you are going to make available through the quest hint system. This is something you should decide upon before you start coding, because it will inform your development if you know what information players are likely to possess. Every room that has unique syntax should also come with a helpfile - this doesn't cover syntax for quests, but it does cover syntax for general functionality. Our helpfile should explain how the referencing system works and what the commands are to use it. Our help system is quite powerful, and one of the most 256

Epitaph Online

http://drakkos.co.uk

useful frameworks we have for reducing player frustration.

Lessons Learned
Without reflection, we are little more than apes. Part of your 'wind up' process should be to compile, either mentally or more formally, a list of the lessons you have learned when putting together your development. These should be corrective rather than congratulatory, although if you feel you've done a good job you should indeed take pride in that. More important than your success though is where you feel you either failed to meet your requirements or struggled with a piece of code or concept. These notes should be for your own personal use, but they can be invaluable in working out how best to support yourself in future. Make a note of all the things you found difficult, and all the things you got working but only after huge trial and error. Especially take note of things that work, but you're not sure you did the right way. All of these are tremendously valuable things to reflect on and ask people about - it's how you make the big leaps in your learning. A lesson's learned list might look something like this: Don't copy and paste add_items, use an inherit. Get the skeleton of the area working after you've written a placeholder inherit.

While these are for your own personal use, they start to expand into a general philosophy for how you should go about developing projects. They're your Documented War Stories. Make a note of them, add the result of your reflection, and use that as the basis to make your next project even better and run even smoother! As time goes by, you'll find that other people can start to learn from the lessons you have learned - it can serve as a useful resource for anyone starting out, and with a bit of editing you may even want to make the results of your reflection available to other creators. That's how you progress from being a Frightened and Young New Creator to being an old, grizzled and practically senile oldbie! For context, pretty much everything I have spoken about in this and related texts is the result of my own lessons learned.

Conclusion
There are certain things you need to do to 'wrap up' your development and put it into the game as a finished product. You need to consider how your development is to be made accessible, and you need to make the necessary information available to other creators. You should really be planning this as you plan the details of the area to ensure you don't have a Horrible Writing Task left to do when you should be celebrating your achievement.

257

Epitaph Online

http://drakkos.co.uk

Additionally, you should spend some time reflecting on what went wrong, what went right, and more importantly why. This will serve as the basis for ensuring that the next project you develop is easier to get a handle on, and as time goes by you'll find yourself internalising these lessons in such a way as to make project development as smooth and painless as possible for you.

258

Epitaph Online

http://drakkos.co.uk

Lessons Learned
Introduction
Now that we have extensively planned out our development, let's reflect on what we have learned! Or not. Sadly, we can't really do that because we haven't yet started to develop our area. Developing is when when we'll see where our plans will need trimmed, expanded and reigned in. Instead, let's reflect on the lessons learned of other people who have been through this process before, and what they've taken away from the experience. What you take away from these lessons is up to you - I would hope at least they'd make you think before taking certain decisions with your code.

Death March Projects


Death March projects are those that have killed creators every game such as ours has at least a few of these, some more than others. Either their scope is so ridiculously large that there's no realistic chance of it being completed in any reasonable space of time, or their feature-set is so complex that it is outwith the grasp of most creators. Death March Projects often overlap with feature creep projects because of the sheer throughput of creators, each adding to the plan and then leaving. The net result of adding a creator is that the end point of the development gets further away. Hopefully your project won't be a death march project, but if it is don't give up hope. Instead, learn from the lessons of those who have gone before you. Don't add to the project instead, recast it entirely. Look through what code has been completed, and figure out the easiest way to take it from development to a finished project. This may mean closing off substantial chunks of it so it can be developed and released incrementally. Breaking a project off into three or four subprojects that can be developed independently and then released is the easiest way to turn a Death March Project into a project for which you are the celebrated hero who conquered it. The problem with such projects is that the plans are always very cool. You read whatever design documents there were, maybe view the board archives, and read the wiki. Wow!', you think, This will be awesome'. But it won't be, because you'll be crushed like everyone else who tried to get a handle on it. Make a realistic assessment of how long it'll take to bring the project into playtesting, and then ask yourself Do I want to be spending that much time before I see any code in the game?'. If the answer is no, then be aggressive cut everything that is peripheral to the main theme of the development. Check with your domain administration 259

Epitaph Online

http://drakkos.co.uk

while doing this they may have opinions on which features are entirely expendable. Sometimes you'll find that the code for your Death March Project is actually impossible to salvage you're left with a horribly ambitious plan and no code! In such cases, with the blessing of your domain administration, you should simply start over. Don't feel as if you are bound to the plans of creators who have gone before they had their chance. A Death March Project is a challenge, but one that everyone is capable of meeting if they tackle it appropriately. Usually the projects are defining features for a domain something so integral in the books that lord after lord has felt compelled to continue development. As such, you're usually getting a pretty sweet brief for your project, it's just the cruft you are inheriting that is the problem. Just remember a development plan is not a binding contract, you are allowed to deviate from it, and scrap it entirely if it's unworkable.

Realism over Fun


Ho boy, are we all guilty of this one. At one point or another almost everyone has said something like I'm going to do this because it's more realistic than this other way of doing it'. This is a warning sign realism should never be the reason why you implement or change something. We're game developers, and Epitaph is not a life simulator. We are unashamedly, openly and without apology a game first and a world second. If you want to make a change, make it on the basis of gameplay, or balance considerations. If your sole answer to being asked Why do it that way' is a variation on the theme of realism, step back, take a deep breath, and don't do it. Realism is largely a code-word for make things fiddly', and we gain no extra game from that. There are many things that are realistic, but in place for a different reason. Sometimes you want to incorporate a little more realism to justify a change you want to make for, say, game balance. That is fine it is realism for the sake of realism that must be avoided at all costs. Consider the streamlined mechanics of something like Warcraft with regards to, say, swimming to those in games with 'realistic' swimming systems. In Warcraft, you don't drown and lose all your stuff. In a 'realistic 'system, that's entirely possible. One is the emphasis of playability over realism, the other is the reverse. Realism should always come as an added benefit for a change to gameplay, not as a reason in and of itself. We have compulsory eating and drinking here. We don't have it for realism, we have it because it is a game mechanic that accomplishes a number of goals we have for the game: The need to scavenge The need to play rather than idle 260

Epitaph Online

http://drakkos.co.uk

The sense of a world where basic survivial is not a given.

Remember what alchemical process we are here to facilitate time goes in, fun comes out. If your changes for the sake of realism don't improve that, then don't put them in.

Randomness Is Not Challenging


Every now and again someone develops something where survival or success is based on the roll of a dice. You either get lucky or you don't... that's just the way of it. While there are a few self-contained games in which chance is the sole driver for winning and losing, they are very much the minority. Randomness does not offer the potential for interesting game decisions. Imagine a situation whereby you must roll a one hundred sided dice. You win the game if you roll a 47. If you want to win the game, you need to roll that dice, and keep rolling it, until you get a 47. You might never roll a 47 you may spend an entire gaming session rolling the damn thing and never have it appear. That's not a challenging game, it's a random game. A challenge is something against which you can pit your own skills and abilities while randomness is often a factor in this, it should never be the sole determiner. Careful consideration and planning should allow you to change the odds in your favour. To make something like the dice-rolling above challenging, it needs to be responsive to strategizing. If you could change the odds by rolling in a certain way, or by blowing on the dice, or doing well, anything that allows for some measure of skill to adjust the outcome that's moving in the right direction.

Complexity is not King


The principle of Keep it Simple, Stupid' is substantially true for Epitaph. Many people confuse complexity with quality, and think that providing complicated, intricate systems is the best way to implement new game features. It's possible to have depth of expression even with simple systems indeed, a good design goal is to make systems as simple as possible, but inter-related. That leads to emergent game behaviour which is always more interesting than simple complexity. Take our material manipulation system for example it's based on tremendously simple rule. Consider working with metals - smelt a nugget of iron and you will get a bar of iron. The quality of that bar of iron depends on the quality of the nugget and your own skills. That is all there is to smelting, but because both the nugget and the iron bar are parts of other systems (gathering and crafting respectively), then there are interesting game decisions that emerge as a result. When adding a substantial new feature, you're not looking to implement it realistically instead, you want the essence of the activity enough that you can genuinely feel part of the fun, but not so much that you are overwhelmed with 261

Epitaph Online

http://drakkos.co.uk

tedious micromanagement. Look at how we are implementing our library a single research command that abstracts the task. We could just as easily have the player check each book in turn, saying You don't find anything about tedium in that book' until they find something interesting, but that's not fun it's micromanagement. If you are adding systems into the game, consider how they could be streamlined lose the jagged edges, and keep the core. You can always add complexity later, it's not always so easy to remove it.

Complaints Are Not Representative


If you handed everyone in the world a bag of money, you'd get people complaining they had nowhere to put it. It's important to realize you are not going to please everyone, and despite the effort you have put into your development people are going to be unkind about it. There's not much I can say about that, you just have to take it in stride. The danger comes in when you take the complaints at face value. Complaints are not generated according to a vote amongst all players, and they certainly aren't quorate. There is a (probably apocryphal) story about President Harry Truman, who growing frustrated by his growing pile of hate-mail turned to his wife and said Why is it only sons of bitches know how to lick a stamp?' It's important not to think that complaints are representative usually they are not, they are usually from the vocal minority rather than the silent majority. That doesn't mean you should ignore them, but you certainly shouldn't ascribe them with any great truth value unless widely corroborated. Not being able to contextualise complaints is likely to grind you down more quickly than anything else. You will find, despite your being a volunteer developer on a completely free gaming environment that some people will still not appreciate the effort you have put into the game (that is, assuming you've been putting effort into the game). These are often the professional malcontents who derive most of their joy in the game from putting down others. In cases where you feel that you are being unfairly criticised, you can always remind people of our guarantee if not completely satisfied, we will refund all of the money they paid us.

Know When to Step Back


On encountering an intractable problem, there are two courses of action: Bang your head against it until you solve it. Go away and come back later.

You should know that the latter of these is not the cheating option'. Sometimes it takes a while for your subconscious mind to tick over and provide you with the 262

Epitaph Online

http://drakkos.co.uk

solution. I can't count the number of times I've had a flash of inspiration in the shower, while driving, or just sitting and reading something else. Your mind is always working away, and avoiding consciously focusing on a problem means you can view it with a fresh eye when you come back to it. Don't feel that you have to keep struggling until you find the answer. Know when to take a step back and let the answer come to you naturally. You'll be happier, you'll be less stressed, and you'll enjoy yourself more. Which brings us to the most important lesson that everyone should learn...

For God's Sake, Have Fun


Don't take it all too seriously you didn't sign up as a creator to not have fun, you signed up to experience fun from a different perspective. Sure, there are lots of rules and guidelines and huge amounts of stuff to learn, but don't lose perspective it's all about having fun. There's a lot of fun to be had from bringing something new into the game and having people enjoy it, and we sometimes lose sight of that when getting frustrated with development setbacks. It's all part of the learning experience. In Software Engineering, there is a stereotype of the guy in the room' tremendously hard working and dedicated, but not actually part of the team. Don't be that guy (or gal). We do suggest quite strongly that you keep your head down for a while until you get a feel for the social environment, but once you feel you have a handle on how creator conversations actually work, then join in! The social context in Epitaph is one of the most rewarding parts of being a creator there are people on Epitaph who have become very real and genuine friends of mine, and one of the things that keeps most of us here are the people we know. You're part of that community, and much of your support and enjoyment will come from participation. Don't forget to have fun if you're feeling unproductive, play for a bit you don't want to lose touch with the game. If one part of your development isn't going the way you want it to, change tack for a while to something more fun. If you remain stressed, or unproductive, or just generally fatigued then take that for the warning sign it is. It may be a hint that you need to step back from the MUD for a while talk to your domain leader if this is the case. Every one of us knows that Real Life Comes First, and we'd all much rather someone took a break than burnt out. If you are just not having fun here, then something is wrong and it needs to be addressed. The reason we are all here is to have fun. That's the most important lesson you can take away with you from this chapter.

263

Epitaph Online

http://drakkos.co.uk

Conclusion
We've reached the end of our journey, save for some concluding remarks in the next chapter. I hope you found it as enjoyable to read as I did to write. Ideally you found it more enjoyable! Life is about learning everything you do provides you with a lesson, and coding on Epitaph is no different. You should be mindful of the lessons you can learn from your development because they're all part of what we refer to as experience'. That's all experience is after all it's what you learned from the mistakes you made.

264

Epitaph Online

http://drakkos.co.uk

Final Thoughts
Introduction
Cor, do you remember when we were wide-eyed and innocent, knowing only that we were going to develop something but with no firm plans as to what? Now look at us we have a firm handle on exactly what shape our development is going to take, and all that's left for us to do is actually code it. Still, as a wise man once said 'Programming is the last mile of game development'. While a lot of people plan as they code, there is much to be said for simply sitting down and thinking through what it is you're going to do. In this chapter we're going to talk about where you go next with your training.

What Happens Now?


Certainly your next step is easy you progress on to Intermediate LPC, in which we'll actually start coding all of the stuff we've been planning. It's quite a complex development involving handlers, inherits and quests by the end of that particular set of material you'll find yourself more than capable of handling most of the tasks that life as a creator may throw at you. You're probably going to have a project of your own you will be working on the best thing to do is apply all the stuff we have talked about to designing out your own development. It's one thing to follow through some worked examples when someone else is doing the thinking, it's another entirely to put your own mind to such a plan. We've talked a lot about game design in terms of how it specifically relates to the MUD as an environment it throws up a lot of interesting challenges that aren't present in other such games. As a developer base we have been learning it all as we go along, and other people may very well hold different views on these topics than I do. You should talk to other people about the things in this material to get a wider perspective than just mine.

Keeping Our Players Happy


Game development is a process of judgments. Not everyone is going to agree with your choices, and not every player is going to find what you offer compelling. That's okay as long as we provide something for everyone as we go along, we don't have to make everyone happy within a single development. If we did, our task would rapidly become impossible. At the core though, what we're looking to do is improve the efficiency of a simple 265

Epitaph Online

http://drakkos.co.uk

process. Our players invest time, we invest time, and what comes out at the end is fun for both. Sometimes we need to reduce the fun of one group of players to enhance the fun of a larger group, or to ensure that fun is had for a longer period of time. All of the most unpopular balancing' decisions that have been taken over the years have been in service of this overall goal. While their effectiveness can be argued, what we should remember at all times is that each and every one of us is committed to making the game better. We may disagree on how that should be done, but we can all agree that everyone has the best intentions. One of the things that being a creator does open your eyes to is how hard it actually is to do the kind of stuff that is so cavalierly dismissed on the boards. It gives you a sense of perspective that a player simply cannot have, because they are not part of the designer equation in our Grand Formula. Balance decisions are tremendously intricate, and they reverberate far beyond the direct change itself. While it is unlikely that your first few developments will have Mud Changing Aftershocks, it is better that you start thinking in those terms sooner rather than later. You should never lose touch with the game itself, but being a creator means being willing to make decisions that will directly disadvantage your player character, in the hope that the overall satisfaction of the playerbase rises.

Conclusion
That's it! Our business is concluded for now! Hopefully you will have found this at least a little bit helpful in managing the complexity that comes along with developing an area in our MUD. You have many constraints to work within - your own confidence with code, the thematic considerations, and the general principles of game design. You're going to make mistakes as you go along, because everyone does - the important thing is that you learn from them. Have fun!

266

Epitaph Online

http://drakkos.co.uk

Section Three

Intermediate LPC
267

Epitaph Online

http://drakkos.co.uk

The Mudlib Strikes Back


Introduction
Welcome to Intermediate LPC - the intermediate level creator material for Epitaph Creators. Before progressing to this material, you should be sure that you understand the material presented in Introductory LPC, and the Being A Better Creator material. For this text, the assumption will be that you have read both of these, and understand them all entirely. This is especially important for the coding side of things - seriously, if you don't understand ever single thing in Introductory LPC, you shouldn't think about attempting this book. I assume a lot of you at this point. Once we progress beyond the basics of LPC coding, it becomes possible to do some very fun things - however, the theory and practise gets considerably more complex when we start talking about commands and quests, so it's important for you to be willing to persevere. Before we progress to the Meat and Taters of this set of learning material, let's recap on what we're going to be doing as our worked example.

Zombieville
We've set ourselves quite the challenge for our second learning village. We've set the theme of an area as a rural village in the wilds of Scotland, complete with an abandoned school containing a secret within. Within the school, we have decided on three quests: Sort the library Break through the partition wall to the corridor beyond Find and unlock the keypad that allows access to the cellar.

In particular, we have set ourselves the task of making these quests, as far as is possible, dynamic. That, more than anything else, provides a substantial challenge to our development skills. We have also decided on a range of NPCs to populate our area. First we have the zombies ravenous corpses looking to feast on the meager nutrition offered by your decaying human brain. We have our survivor, huddled in a knitwear shop awaiting assistance from brave adventurers. For our last NPC, we have the headmistress - our star attraction. She is a dread boss NPC, with powers and capabilities far beyond that of the usual, everyday undead fiend. Finally, we have also decided on some features for the area. We have the library of our school, which is the biggest draw to the village, and our local shop which is 268

Epitaph Online

http://drakkos.co.uk

full of tartan knitwear and schematics. Along the way, we're going to look at building the coding infrastructure of this development. It has quite a complex make-up of features, and we want to be able to make sure it all works correctly and can be changed easily and efficiently. However, what we won't be doing is focusing on the descriptions very much - this is a coding manual, not a writing manual, and as with Deadville our focus is on making the development actually work, not with making sure it reads well. It also won't be 'feature complete' - we don't learn a lot by doing a dozen variations on the same theme, and so we will talk about how to do something once and leave it as exercise for the reader to fill in the blanks.

The Village Plan


We decided upon a layout quite early in Being A Better Creator - to recap:
6 / \ 5 + 7 \ / 4 / 3 | 2 / \ 1 8 \ 9

Our first step is going to be to setup this framework. We're going to do it a little bit differently to how we did Deadville. This is Intermediate LPC after all - we do things Bigger and Better! The biggest change we're going to make is in the architecture - we're not going to use the standard inherits for our rooms, we're going to create our own set of inherits. This gives us tremendous flexibility over shared functionality for rooms and NPCs. That will be our first goal with the village - putting the rooms together. You may think 'ha, I've already done that with Deadville', but not like this you haven't - trust me.

A Word Of Caution
The quests we write as part of this material are not supposed to be tutorials for how to write your own quests. We will cover certain key Epitaph concepts in the process of building these, but you shouldn't think that any quest you write will be written the same way. The important thing in all of this material are the tools and techniques you use, rather than the product you end up building. This is an important point - you shouldn't think of any of this as a blueprint, it's just a 269

Epitaph Online

http://drakkos.co.uk

process that is used to put together a complex area. What you should be paying most attention to is the theoretical asides, such as when we talk about handlers, or inherits - essentially any of the 'science bits'. It is understanding when and where these programming constructs and design patterns should (and should not) be used that is by far the most important element of Intermediate LPC. So please, don't just copy and paste the code that is provided - the code is the thing that is safest to ignore! Why the code was written in the way it was written though - ah, there be knowledge!

Conclusion
Fasten up tight kiddies, there's some pretty treacherous terrain ahead. By the end of this book, if you've understood everything on offer, you'll have the necessary toolkit to be able to code objects that will fascinate and delight young and old. You won't know all there is to know about LPC, but you'll know more than enough to be a Damn Fine Creator. Many creators over the years have been content to write descriptions and develop simple areas, and there is nothing wrong with that. However, when you want to do something that is genuinely cool, you need to know how the code for it is put together. That's what we're here for!

270

Epitaph Online

http://drakkos.co.uk

Inheritance
Introduction
When we built the infrastructure for Deadville, we made use of the standard inherits provided by the game for inside and outside rooms, as well as for NPCs. This is a solid strategy, but one that limits your options for adding in area-level functionality. In this chapter, we are going to build a series of custom inherits for our new and improved area. These will be specializations of the existing inherits (were not going to have to have much code in them), but theyll make our lives easier as we go along. Inheritance is one of the most powerful features of object orientation, and one of the reasons why the Epitaph Mudlib is so flexible to work with as a creator. However, the cost of this is in conceptual complexity - it's not necessarily an easy thing to get your head around.

Inheritance
The principle behind inheritance is simple its derived from the biological principle of children inheriting the traits of their parents. In coding terms, it means that a child (an object which inherits) gains access to the functions and variables defined in the parent (the object from which it is inheriting). That's why functions like add_item work in the rooms we code. Some other creator wrote the add_item method, stored it in the code that defines a room, and as long as we inherit the properties of being a room in our own object, we too have access to that function. If you inherit STD_OBJECT, you will find the add_item function is no longer available. At their basic level, inherits look something like this:
inherit STD_OUTSIDE; void create() { do_setup++; ::create(); do_setup--; // My Code In Here if (!do_setup) { this_object()->setup(); this_object()->reset(); } }

There is no requirement for an inherit to inherit anything itself, but since this is 271

Epitaph Online

http://drakkos.co.uk

going to be our outside room inherit, well take advantage of having an object that already does all the work for us. The create() method is common to all LPC objects its what the driver calls on the object when it loads it into memory. The code here is slightly abstract, so dont worry too much about what it means. In brief, what its doing is making sure that setup() and reset() get called at the right time when an object is created. Without this, you get weird things like double add_items and such. Dont worry about it, it just has to be there. Any code that we would normally put in the setup of an object can go between the two blocks of code, where the comment says my code in here. For example, if you want every room in your development to have an add_item, you can do that:
inherit STD_OUTSIDE; void create() { do_setup++; ::create(); do_setup--; add_item ("zombieville", "Hell yes!"); if (!do_setup) { this_object()->setup(); this_object()->reset(); } }

Instantly, this makes life easier for us. We can have a common set of move zones, add_items, and even functions available to all the objects we create when we use this inherit this rather than STD_OUTSIDE. As a general note of good practise, store all your inherits together so people can easily find them one of the side effects of it being possible to write them is that they can rapidly make it difficult to navigate through an object unless people know where they should be looking. For the sake of convention, all the inherits we talk about in this book will be stored in the /inherits/ sub-directory of our zombieville folder. This one in particular will be stored as outside_room.c.

Hooking It All Up
Our next step then is to make it so thi s inherit is freely available in our project in short, we setup our path.h file. Let's assume you have a ZOMBIEVILLE define somewhere already you know how to do that.
#define #define INHERITS ROOMS ZOMBIEVILLE + "inherits/" ZOMBIEVILLE + "rooms/

Now that we have this, lets put in place the architecture our our new village. We do this the same way as we did for Deadville, except we have a new and exciting inherit of which we can make use: 272

Epitaph Online

http://drakkos.co.uk

Once again, were going to do this for each of our outside rooms and add in the exits to link them up appropriately. You should remember how to do that from Introductory LPC 1 - My First Area. If you dont, go back and read it. Its okay, Ill wait. Okay, having done that, we have the basic skeleton of the village in place. Now, lo and behold, we can take advantage of our inherit by viewing our add_item in all the rooms!
> look zombieville Cannot find "zombieville", no match.

Egads, whats this treachery? LPC, You go TOO FAR!

Replace Program
Well, theres a tiny catch here the MUD has a useful little function called replace_program that it uses (or sometimes doesn't its use is entirely optional and will vary from MUD to MUD, even when they use the same lib). What it actually does is far too technical to go into in detail, but essentially when you have an inherit chain this function looks over the chain for objects that dont really need to be part of it and skips over them. It decides this on the basis of whether or not an object has any functions defined in it. So it sees our inherit, thinks Nah, you dont need that and skips over it. Our add_item then never gets added. We dont want it to do this, so we can get around it in two ways: We can include the line set_not_replaceable (1) in the create function. Include a function as part of our inherit.

I favour the latter of these, for no real reason other than I can never remember the name of the function to call. So for now, lets add a function to our inherit:
inherit STD_OUTSIDE; void create() { do_setup++; ::create(); do_setup--; add_item ("zombieville", "Hell yes!"); if (!do_setup) { this_object()->setup(); this_object()->reset(); } } int query_zombieville() { return 1; }

273

Epitaph Online

http://drakkos.co.uk

Now, when working with your own inherits, updating things gets a little more complicated. You first need to update the inherit, and then update each of the objects using it. You can also use the dupdate command, which updates each object in the inherit tree. Dont do that too often though, because youll end up updating a lot more objects than you need to, and you never know when someone may be in the middle of working with a file you just carelessly updated mid-edit.

Success!
So, you update the inherit, and then update everything in your rooms directory. Magically, your add_item now appears in all its glory!
> look zombieville Hell yes!

Now we are well placed to put in whatever functionality we want in our inherit. We can add move zones (indeed, why not), light levels, room chats anything we feel like. We can make an area-wide search function, or add commands to every room through the inherit. Its enough to make a person giddy with power!
inherit STD_OUTSIDE; void create() { do_setup++; ::create(); do_setup--; add_item ("zombieville", "Hell yes!"); set_light (100); add_zone ("zombieville"); add_zone ("zombieville outside"); if (!do_setup) { this_object()->setup(); this_object()->reset(); } } int query_zombieville() { return 1; }

Having done this for our outside room, we should also make an inside room inherit. Its done exactly the same way except that we inherit a different object:
inherit STD_INSIDE; void create() { do_setup++; ::create();

274

Epitaph Online do_setup--; add_item ("zombieville", "Hell yes!"); set_light (100); add_zone ("zombieville"); add_zone ("zombieville outside"); if (!do_setup) { this_object()->setup(); this_object()->reset(); } } int query_zombieville() { return 1; }

http://drakkos.co.uk

Now, we can use this for each of our inside rooms in the same way we can for outside rooms.

However...
One of the things that makes LPC so powerful is that it's a language that supports the principles of multiple inheritance. This means that rather than having only one object that can be inherited from (as per Java and C#), LPC lets you define an object as inheriting from multiple different parents. Thats extremely powerful, but also full of twisty little mazes and traps. Look at our two inherits above what happens if we want to share functionality between both of them? Like, for example, the query_zombieville function. Its not much of a function, but if it were more substantial (like a piece of complex code attached to a search function), we wouldnt want to copy and paste the code for that. Instead, we make a shared inherit, and have our inside and outside rooms inherit that as well as the core Mudlib object. This inherit isnt going to be an inside room or an outside room. Instead, its going to just be an object we create from scratch it inherits from nothing:
void create() { seteuid (getuid()); } int query_zombieville() { return 1; }

Notice our create method here really, it isnt doing anything at all. We'll talk about uids and euids later in the material, but you can think of that line of code as a kind of 'boilerplate'. That's the only line we need in create - there are no parent 275

Epitaph Online

http://drakkos.co.uk

inherits it has to deal with. All we do here is define a function that we want to be available in both inside_room and outside_room. Well save this shared inherit as zombieville_room.c We then make use of that inherit within inside_room and outside_room. However, this brings us back to our earlier problem with replace_program, so we also change the query_zombieville function with more specialized versions: query_inside_zombieville and query_outside_zombieville.
#include "path.h" inherit STD_OUTSIDE; inherit INHERITS + "zombieville_room"; void create() { // Exactly as before } int query_outside_zombieville() { return 1; }

And:
#include "path.h" inherit STD_ROOM; inherit INHERITS + "zombieville_room"; void create() { // Exactly as before } int query_inside_zombieville() { return 1; }

Thus, an inside room has query_zombieville (from zombieville_room) and query_inside_zombieville (from inside_room). Outside also has query_zombieville, but query_outside_zombieville instead.

Multiple Inheritance and Scope Resolution


Working with multiple inherits in one object causes problems with scope resolution. Specifically, imagine if you had a function with one name in one inherit, and a function with the same name but different functionality in a second inherit. When you call that function on your object, which one is supposed to be used? Theres a little symbol that we use to resolve this youll have seen it in the first inherit code we looked at, and also in Introductory LPC 1:
do_setup++; ::create();

276

Epitaph Online do_setup--;

http://drakkos.co.uk

The :: symbol is the scope resolution operator, and it tells LPC call this method on the parent object. This is fine if theres only one parent, but when there is more than one, we need to refer to them specifically. In outside room:
do_setup++; outside::create(); zombieville_room::create(); do_setup--;

And in inside_room:
do_setup++; room::create(); zombieville_room::create(); do_setup--;

The create methods get called in the order you give them, and the parents are differentiated by their unqualified filenames (their filenames without the directory prepended). If we only wanted to call one creator or the other, then we could do that too by omitting a call to the relevant parent. 'Woah, but back up there, Sparky!', I hear you say. Please, don't call me Sparky, I don't like it. 'Why the hell is the outside one outside::create(), but the zombieville one is zombieville_room::create()?' That is an excellent question, imaginary reader! What LPC is looking for for the left hand side of the scope resolution operator is the file name of the inherit. See, as mentioned in an earlier chapter of Introductory LPC, STD_OUTSIDE is a define that comes from mudlib.h, as is STD_ROOM. They map to different files STD_OUSIDE maps to /mudlib/inherits/outside, and STD_ROOM maps to /mudlib/inherits/room. When we use scope operation, the MUD is looking for 'outside::' and 'room::' respectively. For our shared room inherit, the last part of its filename is zombieville_room, and so zombieville_room:: is how we resolve its scope. The question of how you are supposed to know this is fair its all in mudlib.h, but if you don't fancy trying to work it out from that, you can do something like this:
exec return STD_OUTSIDE; Returns: "/mudlib/inherits/outside"

That won't work for defines that you put in our own code (it's only because every file gets mudlib.h for free that it works in an exec), but you should find it helpful.

277

Epitaph Online

http://drakkos.co.uk

Our Zombieville Room Inherit


Now, the problem we have here is that since our zombieville_room inherit doesnt inherit any of the room code, we cant use any of the normal ro om functions like add_item or such. Or rather, we can but we need to do it a little bit more awkwardly. If we try this, we will get a compile time error:
void create() { seteuid (getuid()); add_item ("stuff", "There is some stuff, and some things."); }

However, we can do something like this instead:


void setup_common_items() { this_object()->add_item ("stuff", "There is some stuff, and some things."); this_object()->add_item ("things", "There are things, amongst the stuff."); }

The this_object function allows us to treat our code as a unified whole - there is no add_item in zombieville_room, but there is in the 'package' of zombieville_room and zombieville_inside_room, as an example. Using this_object() lets us call methods on the whole package, and not on the constituent bits. In our inherits, we can make a call to setup_common_items as part of our code, and it will all work seamlessly. So, for outside_room, our full code is:
#include "path.h" inherit STD_OUTSIDE; inherit INHERITS + "zombieville_room"; void create() { do_setup++; outside::create(); zombieville_room::create(); do_setup--; add_item ("zombieville", "Hell yes!"); setup_common_items(); set_light (100); add_zone ("zombieville"); add_zone ("zombieville outside"); if (!do_setup) { this_object()->setup(); this_object()->reset(); } }

278

Epitaph Online int query_outside_zombieville() { return 1; }

http://drakkos.co.uk

And for inside_room:


#include "path.h" inherit STD_ROOM; inherit INHERITS + "zombieville_room"; void create() { do_setup++; room::create(); zombieville_room::create(); do_setup--; add_item ("zombieville", "Hell yes!"); setup_common_items(); set_light (100); add_zone ("zombieville"); add_zone ("zombieville outside"); if (!do_setup) { this_object()->setup(); this_object()->reset(); } } int query_inside_zombieville() { return 1; }

Now we have an extremely flexible framework. If I want things that are common to outside rooms but not inside rooms, I put the code in outside_room. If theyre for inside rooms only, they go in inside_room. If they should be shared between both, I can put the code in zombieville_room. Notice though that well have a problem if we want to create something that isnt an inside room or an outside room (like an item shop...). Well come back to that later, but its not a problem to add what specific inherits we need to make it all work seamlessly.

Visibility
Now, this system as it stands has a number of issues . Let's say for example that my inherit defines a variable. That variable gets inherited along with everything else, and so any object that makes use of mine can change the state of that variable without me being able to control it. Imagine a simple bank inherit we're writing for our village: 279

Epitaph Online #include "path.h" inherit INHERITS + "inside_room"; int balance; void create() { do_setup++; basic_room::create(); zombieville_room::create(); do_setup--; if (!do_setup) { this_object()->setup(); this_object()->reset(); } } void adjust_balance (int bal) { balance += bal; if (balance < 0) { do_overdrawn(this_player()); } } void do_overdrawn (object ohno) { ohno->do_death(); tell_object (ohno, "Be more responsible with your money!"); }

http://drakkos.co.uk

Here, when someone goes overdrawn, they get killed as should be. However, there's nothing that requires people to go through this adjust_balance method. They can just directly manipulate the balance variable in the code they create:
inherit INHERITS + "crazy_bank_inherit"; void setup() { balance = -1000; }

This is because our variable is publicly accessible, which is how all methods and variables are set as default. Public means that anything that can get access to the variable can manipulate it. I don't want people to be able to do this, because it circumvents the checking I wrote into my method. We can restrict this kind of thing through the use of visibility modifiers, which we will discuss again in Working With Others. By setting a variable to be private, it means that it can only be accessed in the object in which it is defined:
private int balance;

Any attempt to manipulate it in a child object will give a compile time error saying that the variable has not been defined. They can define their own balance variable, but changing that won't change the state of the one in the inheritable. This forces people to go through our adjust_balance method. Any time we protect a variable in this way, we should expose a pair of accessor methods (a setter and a getter) that allow it to be manipulated with our consent:
int get_balance() {

280

Epitaph Online return balance; } void set_balance (int bal) { if (bal < 0) { do_overdrawn (this_player()); } balance = bal; }

http://drakkos.co.uk

In this way, we get the benefit of control over how a variable is manipulated, but we don't restrict the freedom of others to work with the internal state of our object.

The Impact of Change


We will talk more about the impact of change in the next section of this book, but as a hint of things to come, this is why visibility modifiers are important - if we have a public variable in our inherit, we have to assume that somewhere someone is making use of that variable in their own objects, and if we change it we will break their code. That's bad voodoo, man. We limit variables to being private to keep our options open - if I want to change it from an int to a float, then I can't easily do it if it's set as being public. If it's private, then the only code that will break is in the inherit itself, and I can fix that directly. For similar reasons, we may wish to restrict access to methods. We can do that too. Methods can be set to private - once they are, they are no longer possible to call from a child object, or from outside that object with call_other:
private void do_overdrawn (object ohno) { ohno->do_death(); tell_object (ohno, "Be more responsible with your money!"); }

Sometimes though, we don't want to be this restrictive. Maybe we want people who are making use of our inherit directly to be able to access the function, but set it as inaccessible to outside objects. There is a 'middle' level of visibility that handles this - protected. It allows access for the inherit in which the method or variable is defined, and any objects that inherit that code. It does not allow access via call_other or the -> operator - it'll simply return 0 if access is attempted.

Conclusion
Putting in the skeleton of an area becomes much easier when we make use of bespoke inherits. We can bundle common functionality into one piece of code (thus making it much easier to maintain), and we can future-proof our development by making it easier for us to add wide-ranging systems at a later date without needing to change all the other rooms in the development. When you look at cities like Dunglen, or even smaller areas like the Dunglen hospital or the Winchester, youll see they were all written with this kind of system in mind.

281

Epitaph Online

http://drakkos.co.uk

It may seem like overkill to put an architecture like this in place for a small development like Zombieville, but on those occasions where I have not done something similar for even small areas, I have regretted it. Take that advice in whatever way you wish!

282

Epitaph Online

http://drakkos.co.uk

The Library Quest


Introduction
Lets begin our discussion of the content with the school, our quest hub in this particular area. Our first quest, as Im sure you recall only too well, was to sort the library shelves. We enter the room, and the floor is strewn with discarded books. There are bookshelves everywhere, each marked with particular category headings. Putting the books on the floor onto the right bookshelf is our quest. Its a solid quest one that allows for us to put a dynamic framework around it so that it cant be solved simply from a quest list. Because of that, it needs a little bit of thought to structure properly. Setting up the quest also requires us to introduce both the concept of a quest NPC and the structure of a quest as it is interpreted by our quest handler. Alas, the nature of the quest handler means that you cannot easily experiment with quests in your /w/ directory, so you will need to make use of the quests as they are provided to you. Still, never mind you can do whatever you like with the code! So, without any further ado let's get started!

An Epitaph Quest
Many things on Epitaph are handled by files known as data files. They look a lot like virtual files, but work differently they're essentially text files that get turned into LPC objects through the gentle ministrations of a handler called the data handler. When we want to write a quest for Epitaph, we need to make available a quest file for it that registers it with the quest handler and let's us set up all its pertinent data such as its story, quest hints, reward levels, criteria and so forth. A simple such quest file might look like the one in /data/quests/librarian.que:
::item "visiting librarian":: ::->name:: "visiting librarian" ::->story:: "obeyed the categorical imperative" ::->text:: "It appears that the library is still on Anges McKay's mind. It would be an act of considerable kindness " "if someone were to organise it and put her troubles to rest. The ones of which she is aware, anyway..." ::->rewards:: ([ ]) ::->consumed:: ([]) ::->criteria:: ([ "organised library" : 1, ]) ::->completion_message:: "It seems like a a futile way to spend an afternoon

283

Epitaph Online

http://drakkos.co.uk

during the apocalypse, but perhaps by " "small acts of kindness like this, some of our humanity may be retained..\n" ::->hints:: ({ }) ::->region:: "zombieville" ::->subarea:: "zombieville" ::->luck_needed:: QUEST_MINIMUM ::->randomness:: QUEST_MODERATE ::->player_skill:: QUEST_MODERATE ::->character_skill:: QUEST_NONE ::->listability:: QUEST_NONE ::->time_needed:: QUEST_MODERATE

You should get comfortable with this kind of file, because we use them a lot on Epitaph. You don't need to worry too much though about what's happening, just know that each of the little ::-> symbols indicates a piece of data about what makes up a quest. Data files drive most of our configuration details every kind of material is a data file, every quest, every achievement, every faction mission, every knack so many things, in fact, that I can't really give an accounting. List the contents of /data/ to see how many things get represented like this. I won't spend any time explaining what the individual bits do they should be obvious from context. The only ones that really matter for our purposes are the name and story (which we agreed on in the last section), the criteria (which is how we'll be able to tell if a quest is completed or not), and the various entries at the bottom these are the things that are used to calculate the level of the quest. We don't need perfect values for any of these yet, we just need values, and these are the ones we have. In a very real sense though, this is our quest the code that we still have outstanding is what allows a player to go from having no quest information at all to having the right criteria for the quest to be completed.

Data Representation
Of all the decisions that a coder makes, how they choose to represent data is the most important. Its what changes an impossible task into a trivial task, or importantly - a trivial task into an impossible task. If you choose good data structures for how you store and manipulate all of the data associated with a system, your job becomes exponentially easier than if you choose bad data structures. When we talk about data structures, we mean the combination and interrelationship of data-types within an LPC object. An array of mappings is a data structure. A mapping of mappings is a data structure. Ten ints and a string are a data structure. Our choice in this respect should be influenced by several factors: How easy is the data structure for others to read and understand? 284

Epitaph Online

http://drakkos.co.uk

How efficient is the data structure in terms of memory and CPU cost? How simple is it to query the various parts of the data structure? How simple is it to manipulate the various parts of the data structure?

The requirements for each of these factors will vary from situation to situation. Core Mudlib code must be highly efficient and simple to query and manipulate. It doesnt have to be especially readable since the people likely to be working with it are usually more technically experienced, but make no mistake readability helps. On the other hand, code written for these tutorials must emphasise readability, and readability is one of the key predictors of how maintainable code will be. For normal, every day domain code where you cant assume a certain level of coding knowledge, readability is a hugely important feature. Our decision as to how to represent our data will emerge out of a consideration of the data itself what do we need to store, for how long, and in what ways do we need to manipulate the data? Lets consider our library quest. We We We We We We We We We We need need need need need need need need need need to to to to to to to to to to store the titles of some books. be able to get a random list of these store some category headers. be able to get the list of these store how a player has sorted books know which category a player has associated with each book let players move books from category to category let players reset their entire categorization. let players add books to categories let players remove books from categories

Its apparent here that the first two sets of data require little manipulation, and the last set of data requires considerable manipulation. If we change the parameters of our quest, we also change the nature of the data representation. For example, say we let people add their own books (a bad idea, but lets say we did). In addition to the requirements above, we would also need to allow players to add titles to the list of books, and remove titles from the list of books. Wed also need to be able to save the data note that we dont need to do that at the moment. Changing the parameters of the quest will have an impact on how simple the data is to manipulate. If youve chosen a good data structure for your purposes, it will be possible to add new functionality as time goes by with minimal fuss. If youve chosen a bad data structure, then new functionality becomes very difficult to implement. That's what we mean by 'maintainability'. Lets look at two ways of representing this data. First, a straw-man to show the impact of bad data representation:
string book1; string book2;

285

Epitaph Online string book3; string book4; string category1; string category2; string category3; string category4; mapping books_and_categories; void create() { seteuid (geteuid()); } void setup_quest() { book1 = "Some stuff about book2 = "Some stuff about book3 = "Some stuff about book4 = "Some stuff about category1 = "romance"; category2 = "action"; category3 = "erotica"; category4 = "comedy"; you."; me."; her."; him.";

http://drakkos.co.uk

books_and_categories[book1] books_and_categories[book2] books_and_categories[book3] books_and_categories[book4] }

= = = =

category1; category3; category2; category4;

This kind of data representation does not lend itself well to modification or expansion. What happens if you want to add in twenty new books? What happens if you want books to belong to more than one category? What happens if you want people to be able to sort the list of books so they can browse through it easier? None of these things are simple to do with this bad data structure. A slightly better one:
string *books; string *categories; mapping books_and_categories; void setup_quest() { books = ({"Some stuff about you.", "Some stuff about me.", "Some stuff about her.", "Some stuff about him."); categories = ({"romance", "action", "erotica", "comedy"}); books_and_categories[books[0]] books_and_categories[books[1]] books_and_categories[books[2]] books_and_categories[books[3]] } = = = = categories[0] categories[2]; categories[1]; categories[3];

It's still not great we have to hand roll the connection between each book and each category. How about this:
mapping books_and_categories; void setup_quest() { books_and_categories["romance"] = ({"Some stuff about you"}); books_and_categories["comedy"] = ({"Some stuff about me"}); books_and_categories["erotica"] = ({"Some stuff about her"});

286

Epitaph Online books_and_categories["action"] = ({"Some stuff about him"}); }

http://drakkos.co.uk

Here, were going a slightly different way if we ever want a list of the categories, we need to pull it out of the data structure like so:
string *query_categories() { return keys (books_and_categories); }

Weve traded off a little efficiency in favour of a more elegant representation. Now, if we want to add a pile of new books, its a trivial task
void setup_quest() { books_and_categories["romance"] = ({"Some stuff about you", "Gnoddy", "Things that go bump in the day"}); books_and_categories["comedy"] = ({"Some stuff about me", "Where's My Cow?"}); books_and_categories["erotica"] = ({"Some stuff about her", "Things that go bump in the night"}); books_and_categories["action"] = ({"Some stuff about him", "The Nevewending Story"}); }

There are better ways still to do this our structure is efficient, but its not especially expandable. What happens if we want to make the books a bit more interesting? If for example we wanted to add the name of the author, or a blurb on the back? Thats not easy to do with our current representation we need something different.

In A Class of Your Own


LPC makes available a special kind of user-defined data type called a class. For those of you with any outside experience of object orientation, please don't make any assumptions about the word 'class' - it's nothing like a class in a 'real' object oriented language. With a class, you create a single variable that has multiple different compartments in it. For example, we could do this:
class book { string title; string author; string blurb; string *categories; }

With this definition weve created a brand-new data type for use in our object we can designate things as being of type class book:
void setup_quest() { class book newBook;

287

Epitaph Online }

http://drakkos.co.uk

We create new variables for a class in a different way from most variables we use the new keyword:
newBook = new (class book);

Once we have the new 'instance' of this class, we can set its individual constituent parts using the -> operator:
void setup_quest() { class book newBook; newBook = new (class book); newBook->title = "Some stuff about you"; newBook->author = "You"; newBook->blurb = "An exciting tale of excitement and romance!"; newBook->categories = ({"romance"}); }

You can also combine the steps of creating a new class and setting its elements like so:
class book newBook; newBook = new (class book, title: "Some stuff about you", author: "You", blurb: "An exciting tale of excitement and romance!", categories: ({"romance")) );

A single variable of a class is useful, but when combined in an array format they become especially powerful. What we get is something akin to a simple database. Imagine the following data representation:
class book { string title; string author; string blurb; string *categories; } class book *allBooks = ({ }); void add_book (string book_title, string book_author, string book_blurb, string *book_categories) { class book newBook; newBook = new (class book, title: book_title, author: book_author, blurb: book_blurb, categories: book_categories); allBooks += ({ newBook });

288

Epitaph Online } void setup_quest() { add_book ("Some stuff about you", "you", "A exciting tale of excitement and romance!", ({"romance"})); }

http://drakkos.co.uk

Now, when we want to add a new book, we just call add_book as many books as we like. However, if we want to get a list of categories, we have traded off the simplicity of arrays for something that needs a more bespoke solution:
string *query_categories() { string *cats = ({ }); for (int i = 0 ; i < sizeof (allBooks); i++) { foreach (string c in allBooks[i]->categories) { if (member_array (c, cats) == -1) { cats += ({ c }); } } } return cats; }

A structure like this requires some management functions for ease of manipulation. For example, what if I want to get the blurb that belongs to a particular book? I need a way of finding that book element in the array, so I need a function to do that:
int find_book (string title) { for (int i = 0; i < sizeof (allBooks); i++) { if (allBooks[i]->title == title) { return i; } } return -1; }

Then, if I want to query the blurb of a book:


string query_book_blurb (string title) { int i = find_book (title); if (i == -1) { return 0; } return allBooks[i]->blurb; }

Theres some more upfront complexity here, but the trade -off is that its much easier to add in new functionality as time goes by. If we want to add in a new set of information to go with each book, we just add it and its there and supported. Things get a bit trickier when we load and save classes, but that's a resolvable issue.

289

Epitaph Online

http://drakkos.co.uk

The Library State


So, that sets us up with something that stores each of the books. How do we represent how the player has currently sorted the library? Here, we need to answer a new question do we store this information about the player, or do we store it about the room? If we store it about the player, it means that multiple players can be working in the same library without their actions impacting on the state of the other players. If we store it about the room, then all people working within the library have a shared library state. Really, there isnt a right answer here but since its a little bit weird if everyone is working within their own 'meta' library, lets just decide were going to store the state about the room so when one player sorts a book on a shelf, it changes the state of the room for everyone else in it. That means we need some more variables in our room to store which books are where. We can store that pretty simply as a mapping book title X is on bookshelf Y:
mapping sorting; int assign_book_to_shelf (string book, string category) { int i = find_book (book); string *cats; if (i == -1) { return -1; } cats = query_categories(); if (member_array (category, cats) == -1) { return -2; } sorting[book] = category; }

Believe it or not, thats the bulk of the engine of the quest done. Everything else proceeds simply from this point because we have a solid data representation we can rely upon.

Dynamic Quest Design


Ideally, a dynamic quest will involve some kind of random setup of the books. For example, lets say that our book titles do not give the clue to what the category is, only the blurb does. With this comparatively minor modification to the concept, we can then randomly assign each book a category (and thus a blurb) when we add it. Well, thats no problem at all:
void add_book(string book_title, string book_author) {

290

Epitaph Online class book newBook; string my_blurb; string *cats = ({"romance", "action", "childrens"}); string my_cat; string *valid_blurbs;

http://drakkos.co.uk

mapping blurbs = ([ "romance" : ({ "A romantic tale of two estranged lovers", "A heartbreaking tale of forbidden love!", "A story of two monkeys, with only each other to rely on!", }), "action" : ({ "A thrilling adventure complete with a thousand elephants!", "An exciting story full of carriages-chases and brutal sword-fights!", "A story of bravery and heroism in the trenches of Koom Valley!", }), "childrens": ({ "A heart-warming tale of a fuzzy family cat!", "A story about a lost cow, for children!", "A educational story about Whiskerton Meowington and his last " "trip to the vet!", }) ]); my_cat = element_of (cats); valid_blurbs = blurbs[my_cat]; my_blurb = element_of (valid_blurbs); newBook = new (class book, title: book_title, author: book_author, blurb: my_blurb, categories: ({my_cat})); allBooks += ({ newBook }); }

Now we have a quest architecture in place we give in a list of titles and authors of books, and it sets up the details randomly from that. People will need to read the blurb to find out what the book is about and then sort it into the appropriate category. Theres no way to short -cut this quest, you need to actually put in the work yourself. Sure, someone may quest-list all of the blurbs and the categories to which they belong, but its pretty obvious from the blurb anyway. There is simply no value in quest-listing this. We can add in as many books as we like here, but we want to set some kind of sensible limit for our players say, ten books to give a reasonable amount of work to do before the quest is awarded.

The Rest of the Quest


This is the core of the quest, but we also need to provide the bits around the edges we need to be able to give lists of information on request. For example, 291

Epitaph Online

http://drakkos.co.uk

the list of all books that have not yet been sorted:
string *query_unsorted_books() { string *unsorted = ({ }); for (int i = 0; i < sizeof (allBooks); i++) { if (!sorting[allBooks[i]->title]) { unsorted += ({ allBooks[i]->title }); } } return unsorted; }

A way of telling to which category a book has been sorted:


string query_bookshelf_of_book (string title) { return sorting[title]; }

And a way to reset the sorting if its all gone horribly wrong:
string reset_sorting() { sorting = ([ ]); }

There may be more we need, but these will do for now. We cant anticipate everything, after all. This may not look like any quest with which you are familiar, and thats because its not something like this sits behind every quest in the game, but thats for our eyes only. For the players, we need to give an interface to our code a way for them to call functions in a sanitized manner. For example, we may give the player a command in a room:
sort book_title into category <category>

Upon typing this command, it hooks into the functions weve written specifically, it will do the assign_book_to_shelf function, providing the book title and category they give. We need to then give meaningful output to the player indicating what has happened. Thats for another chapter though.

Conclusion
This chapter has introduced a new, powerful data-type to you the class. I am a big fan of classes, they make almost everything easier to do. However they are syntactically distinct from the other data types with which you will be familiar, and so you should make sure you are comfortable with how they work before you start playing about with them. Data representation is a hugely important topic get your representation wrong, and the rest of your code is doomed to be a hopelessly unreadable, unmaintainable mess. Get it right, and good code will flow like honey. Thats just the way of it. 292

Epitaph Online

http://drakkos.co.uk

Adding Commands
Introduction
We've built the bulk of the technical architecture for our first quest, but what we need to do now is build the user interface to that architecture. The phrase user interface' usually conjures up images of windows based applications, but all it means is what sits between my code and my users'. In the case of a MUD, the user interface may be an item, it may be a room, it may be a command, or it may be something else. What we need to decide then is how we are going to allow our user to interact with the functions we have written. As with most things, this will be influenced by the context of the code there is no definitively right situation.

Deciding On a User Interface


There are certain elements of building a user interface that are universal rules to which we must adhere. In fact, it's not a mandate we have all sorts of things in the game that violate these. However, if you break them knowingly then someone (that is, me) will cut you. A good user interface has the following traits: Consistency Predictability Allows for undoing mistakes Provides meaningful feedback to users

There are other traits consistent to all user interfaces, but these are the ones that we need to view as iron-clad rules for Epitaph development. A good user interface is consistent. That means that if similar or identical functionality is available in a different part of the game, our functionality should mirror the way it's accessed. For example, if we have a mail-room in every city in the game, then it's violating user interface design if one is accessed through commands and another requires you to talk to an NPC. We violate this rule all the time, but those violations should be considered bugs it's not okay to go against consistency. We should always adopt the best interaction choice and be consistent with how it is applied. This is one of the reasons why we like to bundle significant features into inherits that get used across the game. Predictability relates to how the cues that we provide in the game should be interpreted. Imagine if we had a parcel that when unwrapped yielded not a gift but 293

Epitaph Online

http://drakkos.co.uk

instead a package of spiders that ate your face. That may be funny, but it's not justifiable unless there is some kind of predictability built into the parcel. Perhaps if you look at it close enough you get a clue as to what is within, or if you leave it long enough you can see it moving, or hear legs within. If you provide a course of action to the user, the result must be knowable otherwise you are punishing people for exploring your code. As a second example of this, imagine a situation in which we have a game full of red doors and green doors. The red doors do some damage to you and yield a reward, and the green doors permit access with no downsides. This is part of the user interface since it is from this that the user builds their vocabulary of experience. If you then suddenly half way through the game reverse these colours, then the effect is you tell your player Anything you think you have learned may be discarded at any time'. The user then has no way of making meaningful decisions regarding their choice of interacting with the game. It is important that for everyday usage there is a mechanism for undoing changes. We have a high consequence' model for certain things in the game rearranging is easy to do and costly in terms of finite resources to undo, and you can't easily undo a bad choice in learning skills. This is fine, but something to do sparingly. For day to day interaction with the game, there should be an easy way to undo anything that a player has done. For our quest, we make it easy for people to reassign books from category to category, as well as start over. Finally, your user interface should provide meaningful feedback to a player. Imagine if whenever we try to shelve a book incorrectly we get the following message:
> Try again.

How do I choose to make a meaningful decision here? Is the problem with my syntax? Is it with the category I'm using? Am I not using an existing book? Is the book already shelved? There is no way I can get from this error message to a useful change in my behaviour. Thus, when you provide feedback to the user it should indicate in a meaningful way whether there is success or failure. It should also speak in language the player is likely to understand. Object reference is inconsistent with expected parameters' may make sense to a developer, but a player would much prefer something like that particular item isn't what you need here'. Quests on Epitaph don't have a cast-iron interaction convention, but the majority of them are accessed through commands defined in rooms or items or NPCs. Because that's a common interaction context, that's the choice we too will make. In order to do this, we need to talk about a new Epitaph construct, the add_command.

294

Epitaph Online

http://drakkos.co.uk

Adding Commands
You've already done this in a simple way in Introductory LPC, where we added commands to add_items. Epitaph offers a powerful system for making available commands to your players, and it's called add_command. Alas, its power ensures that it's one of the hardest things to use properly, so we must spend a considerable amount of time talking about the theory behind the function. At its basic level what it lets you do is provide a command that exists only in a certain context and as long as a certain condition is true. Usually that context is while the player is either: Inside the object (rooms) Holding an object (items and NPCs) In the same environment as the object (items and NPCs)

You don't need to worry about keeping track of this, it's all done for you by the Epitaph parser. All you need to know is how to add the commands. It's the parser that is responsible for taking what a user types into the game and breaking it into different parts. You don't need to worry yourself overly about this, just treat it as a bit of magic for now just imagine it as that a player types in a string such as stab drakkos with knife' and the parser breaks that up into the command (stab), the objects involved (drakkos and knife) and the rest of the text which is just for aiding in making our commands a little more natural' to type in. The exact way in which the parser does this is decided by the pattern we give the command we add. We can decide specifically what kind of arguments that an add_command should accept and work with. Traditionally, an add_command is located in the init method of the object with which you are working. Let's look at one simple example of this before we get into the specifics. This would be a command that would exist on a room:
void init() { ::init(); add_command ("test", "<string>"); }

And the the function that goes with this add_command:


int do_test(object *indirect_obs, string dir_match, string indir_match, mixed *args, string pattern) { printf ("Test: %s\n", args[0]); return 1; }

In init, we add the command it's called test' and the pattern is any string of text it matches anything that follows the word test'. When we use the comman d, the MUD looks for a function called do_<command>, where <command> is what we named the command. We can change this default behaviour, but we won't talk 295

Epitaph Online

http://drakkos.co.uk

about that just yet. Our command is called test, so the MUD looks for a function called do_test to handle it. When we enter a command in this way, the function do_test gets called, with all the parameters in the parameter list above (they get handled for you). In this function, we simply print out the text that the user typed while in the room (that's what printf lets you do):
> test This is a test! Test: This is a test!

Each of the parameters to the function holds a different piece of information. We'll talk about indirect and direct objects later for now the only one we are interested in is args. This array holds each of the different parts of the command that followed the name. Since we only have a string, that's all it shows. However, consider if we had the following as a pattern:
void init() { ::init(); add_command ("test", "<string> with the <string>"); }

The pattern acts like a filter and a fill in the blanks' system the user must type out this pattern exactly, but they can put whatever they like in the dynamic bits the bits marked with <string>. So the following would all be valid commands:
> > > > test test test test drakkos with the cakes cakes with the tea tea with the toasting forks a whole load of people with the standard examination

However, none of the following would be valid:


> test drakkos > test with the toasting forks > test drakkos with the

When we try to enter an invalid combination of command and text, we get the standard error message:
See "syntax test" for the input patterns.

When the do_test function gets called with a valid command string, our code above only picks up the first string that was part of the match:
> test here with the thing Test: here

If we wanted both bits, we'd need to change out functiona little:


int do_test(object *indirect_obs, string dir_match, string indir_match, mixed *args, string pattern) {

296

Epitaph Online

http://drakkos.co.uk

printf ("Test (First argument): %s\n", args[0]); printf ("Test (Second argument): %s\n", args[0]); return 1; }

Args thus contains an array of each specific 'blank' that the user gave to our pattern. I'm sure you can see why that is useful! Now, let's extend our simple example a little by incorporating meaningful user feedback. We do this through the use of a method called add_succeeded_mess:
int do_test(object *indirect_obs, string dir_match, string indir_match, mixed *args, string pattern) { add_succeeded_mess ("$N $V the command.\n", ({ })); return 1; }

The $N and $V are examples of tokens that get processed by the MUD and turned into meaningful output depending on who is seeing the message. You will see:
You test the command.

Other people would see:


Drakkos tests the command.

The $N gets replaced with the short of the person performing the command, and the $V gets replaced with the verb used (in this case, test'). Pluralisation is done automatically depending on who is doing the observing.

Failure Messages
The final thing we need to discuss about this simple example of an add_command is the return value. This is meaningful a return of 1 means that the command succeeded and that a succeeded message (added by us) should be displayed. If it returns 0, it means the command failed and that a failed message should be displayed, like so:
void init() { ::init(); add_command ("test", "<string>"); }

int do_test(object *indirect_obs, string dir_match, string indir_match, mixed *args, string pattern) { if (args[0] == "pass") { add_succeeded_mess ("$N $V the command.\n", ({ })); return 1; } else {

297

Epitaph Online add_failed_mess ("You failed the test.\n", ({ })); return 0; } }

http://drakkos.co.uk

A failed message goes only to the player, and should be used when the actual functionality of a command has failed to begin - for example, if they were using inappropriate objects, or they used an inconsistent pattern. When you return 0 from an add_command function, the MUD keeps checking for another object that may be able to handle the player's input. If you return 1, it stops checking for another object to match the command. In this way, multiple objects can define commands with the same name, and only those appropriate to a situation will be triggered. There is another way to handle failure messages, and this should be done in situations where there are multiple failures that a player must correct. To illustrate what I mean by this, let me perform a little one man play for you:
> kill midget with sword You cannot kill the midget with the sword, because you are not holding the sword. > hold sword You hold the sword in your right hand. > kill midget with sword You cannot kill the midget because you are lying down > stand You stand up. > Kill midget with sword You cannot kill the midget because you are on fire. > shout AAAAAAAAAAAAAAAA! You shout: AAAAAAAAAAAAAAAA!

This kind of thing can be tremendously frustrating it would be much better if we got a message that listed all the things we would need to correct in order to perform the action:
> kill midget with sword You cannot kill the midget because you are not holding the sword, you are lying down, and you are on fire.

That way, we have a list of things we can correct without having to fix one thing and then retry. We do this through a system of accumulating failure messages, using the function add_failure_reason(), like so:
if (this_player()->query_position ("lying")) { this_player()->add_failure_reason ("you are lying down"); }

298

Epitaph Online

http://drakkos.co.uk

Once we have done all these pre-checks, we provide a final failure reason like so:
if (sizeof (this_player()->query_failure_reasons())) { this_player()->add_failed_mess (this_object(), "You cannot $V $D, " "because $reason$.\n", ({ })); }

The result is a much more helpful, much less frustrating trial and error system, and that leads to a correspondingly friendlier user interface. We'll talk about the array provided to each of the add_x_mess methods a little later in this chapter.

More on Patterns
The real power of add_command comes from the fact we are not restricted to matching strings - we can also make it match other things. A list of matching codes and the things they are used for: Code <indirect> <direct> <number> <word> <fraction> <preposition> Matches An object The object in which the function associated with a command is defined. A number (which must be expressed in digits) A single word A fraction, such as 3/4, 5/6, etc Such as to', from' etc

We can also specialize these patterns, depending on what they actually are. We can do so by separating following specializations with a : . Usually you do this for objects rather than the other types, but the help file for add_command will give you some things you can do with strings and numbers. For indirect and direct, you will often want to specify what kind of object you are are trying to match. The default is any object at all, but you can override that by providing a second part to the match:

299

Epitaph Online

http://drakkos.co.uk

Code object living distant-living any-living

player wiz-present

Matches Any object A living object Uses the function find_living to match an object. Matches living objects in the room first, and then distant objects later. Also allows for the user of someone, everyone and creators as valid matches Only matches a player Wiz present allows for the complex creator level matching. See help wizpresent for details.

We can specialize our patterns with a colon, so:


<indirect:living>

Or
<direct:object>

We can also further specialize these by specifying where a match should be made from: Code me here here-me me-here direct-obs Matches The inventory of the living object performing the command The inventory of the environment of the living object Check the environment first, and then the inventory The inventory of the object, and then the environment. The inventory of the direct object

So, if I was in the strange situation of wanting to match any living object in my inventory, I could use the following:
<indirect:living:me>

Or if I wanted to match players in my environment only:


<indirect:player:here>

The syntax of the command gets built automatically from our pattern (so when players do syntax command', they get the proper information). However, we can make them more readable by providing a label that gets used instead of the pattern: 300

Epitaph Online

http://drakkos.co.uk

<indirect:living:here'pets'>

The text in apostrophes is what will be displayed to the user instead of the pattern itself.

Optional Parameters and Set Choices


We also have the option in a pattern of providing optional parameters (ones that allow the command to flow properly), and choices from a restricted set. Optional parameters are indicated by square brackets, and set choices are indicated by braces:
add_command ("get", "<string> from [the] thing");

The the' is not required in order to make the pattern match, but it won't cause a problem if it's present. The optional parameters get ignored they don't get sent into the parameters of the function. If we want to provide a set of choices, we can do that too:
add_command ("paint", "wagon {red|blue|green}");

This will match any of the following:


paint wagon red paint wagon blue paint wagon green

Nothing else will match. The exact option the player chose will be included in the args array that gets passed as the fourth parameter to the function.

Direct and Indirect Objects


The different between these is not necessarily obvious. The direct object is the one in which the function for handling the command resides it will almost always, unless you are doing something clever or weird, be this_object(). For example, if you have a command defined in a room, then the room will be the direct object. If you have a command defined in a weapon, then the weapon will be the direct object. My advice is, don't worry about it until such time as you encounter a situation where an indirect object isn't working the way in which you need. You could use it productively in the meantime as a resolution for which object to which you are referring. If you're wearing a pair of boots:
add_command ("kick", "<indirect:living> with <direct:object>");

301

Epitaph Online

http://drakkos.co.uk

This would work almost the same as just using a string ("<indirect:living> with boots") except that it allows for more complex pattern matching and gives you the object of ensuring that the boots are being worn and such. Indirect objects are everything else anything that's used by a command but isn't the object in which the command is defined.

Passed Parameters
Now that we've spoken a bit about how add command works, let's talk about the parameters that get passed to the function. There are five of these, and they each contain different information. The first parameter is an array of indirect objects all the objects that matched the pattern that we set. So if we have a pattern of <indirect:object:me> and someone uses sword' for that, it gives us a list of all the object s in the player's inventory that matches the word sword'. If we have more than one check for indirect objects in our pattern, we get an array of arrays instead, each array containing a list of objects that matches the pattern. For example:
add_command ("cut", "<indirect:living> with <indirect:object:me>");

Our player has this command, and types cut drakkos with knife'. Our first parameter then would contain an array of arrays, the first containing all the living objects that match the name drakkos', and the second containing all the objects in the player's inventory matching the name knife'. These get passed in the order in which the objects were matched. The second parameter is the string that was matched for any direct:object pattern. You can safely ignore this for now. The third parameter is the list of string matches for indirect objects. Ignore that too for the moment. The fourth parameter contains all the arguments that the user provided to the command they're the blanks that the user filled in. They get populated in order, so if the pattern is:
add_command ("test", "<string> with <string> and <string>");

And the command is:


> test bing with bong and bang

The args array will be:


({"bing", "bong", "bang"});

302

Epitaph Online

http://drakkos.co.uk

Finally, the last parameter is the pattern that matched the user input. If a command has many different patterns (which is common in guild commands and such), this is the one that met the requirements of the user input.

Tannah's Pattern Matcher


Adding commands and working out what the parameters are is horrendously complicated. Luckily, Tannah@Discworld, back in the dawn of the world, wrote a wonderful tool for helping you get it clear in your head Tannah's Pattern Matcher, which may be found in /items/creator/ matcher. The matcher allows you to add commands to it and then execute them as a result, it gives you what the parameters are for the command as added. Let's look at a simple example of using it:
> add command bing with pattern "<indirect:any-living>" You add the command "bing" with the pattern "<indirect:any-living>" to Tannah's Pattern Matcher. > bing citizen Indirect objects: ({ /* sizeof() == 1 */ /d/forn/genua/chars/citizen#5116424 ("citizen") }) Direct match: 0 Indirect match: "citizen" Args: ({ /* sizeof() == 1 */ "citizen" }) Pattern: "<indirect:any-living>"

I honestly can't emphasise enough how useful this can be to see what the impact of various kinds of patterns are when using add commands. My advice is to clone yourself one of these and try it out with every combination you can imagine until you are 100% sure of what the patterns are and how they work. It'll make the rest of your life as a creator so much easier. When you read the matcher, it will show you what commands you have added:
You read Tannah's pattern matcher: The pattern matcher is currently set to test the following commands and patterns: [0] "bing", "<indirect:any-living>" See 'syntax add' and 'syntax remove' to modify the list.

Make this tool your friend, you won't regret it!

303

Epitaph Online

http://drakkos.co.uk

Conclusion
Add command is the tool that lets you add worlds of functionality to your objects. As befitting a complex, powerful function it's not the easiest thing in the world to learn how to use. However, once you have mastered it you will find it a breeze to add complex, interesting functionality to everything you create. Next we'll look at how we use add_command to build the user interface for our library quest, so make sure you understand what we've spoken about in this chapter. Read it over a few times if the next chapter doesn't make sense - the assumption will be that commands, functions, the parameters they use, and the patterns are all clear to you.

304

Epitaph Online

http://drakkos.co.uk

The Library Room


Introduction
Now that weve spoken in some depth about add_command and how it works, we can start to build the front-end of our library quest. For this, we need a room (the library) which will contain the usual Room Related Things as well as the code we developed to handle the manipulation of the books and categories. Its our job now to craft a compelling environment in which players can easily participate in the quest.

The Room
The room were going to develop is going to inherit from the inside room inheritable we created earlier. We need to do a little retrofitting and expansion to make our room work properly, but our starting point will look like this:
#include "path.h" inherit INHERITS + "inside_room"; class book { string title; string author; string blurb; string *categories; } class book *allBooks = ({ }); mapping sorting; void reset_sorting(); void setup_quest(); void setup() { set_short ("main library"); add_property ("determinate", "the "); set_long ("This is the main part of the library. "discarded books thrown everywhere!\n"); set_light (100);

It's a mess, with "

add_exit ("west", ROOMS + "zombieville_09", "door"); setup_quest(); reset_sorting(); } void add_book(string book_title, string book_author) { class book newBook; string my_blurb; string *cats = ({"romance", "action", "childrens"}); string my_cat; string *valid_blurbs; mapping blurbs = ([ "romance" :

305

Epitaph Online ({

http://drakkos.co.uk

"A romantic tale of two estranged lovers", "A heartbreaking tale of forbidden love!", "A story of two monkeys, with only each other to rely on!", }), "action" : ({ "A thrilling adventure complete with a thousand elephants!", "An exciting story full of carriages-chases and brutal sword-" "fights!", "A story of bravery and heroism in the trenches of Koom Valley!", }), "childrens": ({ "A heart-warming tale of a fuzzy family cat!", "A story about a lost cow, for children!", "A educational story about Whiskerton Meowington and his last " "trip to the vet!", }) ]); my_cat = element_of (cats); valid_blurbs = blurbs[my_cat]; my_blurb = element_of (valid_blurbs); newBook = new (class book, title: book_title, author: book_author, blurb: my_blurb, categories: ({my_cat})); allBooks += ({ newBook }); } string *query_categories() { string *cats = ({ }); for (int i = 0 ; i < sizeof (allBooks); i++) { foreach (string c in allBooks[i]->categories) { if (member_array (c, cats) == -1) { cats += ({ c }); } } } return cats; } void setup_quest() { add_book ("Some stuff about you", "you"); } int find_book (string title) { for (int i = 0; i < sizeof (allBooks); i++) { if (allBooks[i]->title == title) { return i; } } return -1; } int assign_book_to_shelf (string book, string category) { int i = find_book (book);

306

Epitaph Online string *cats; if (i == -1) { return -1; } cats = query_categories(); if (member_array (category, cats) == -1) { return -2; } sorting[book] = category; } string query_book_blurb (string title) { int i = find_book (title); if (i == -1) { return 0; } return allBooks[i]->blurb; } string *query_unsorted_books() { string *unsorted = ({ }); for (int i = 0; i < sizeof (allBooks); i++) { if (!sorting[allBooks[i]->title]) { unsorted += ({ allBooks[i]->title }); } } return unsorted; } string query_bookshelf_of_book (string title) { return sorting[title]; } void reset_sorting() { sorting = ([ ]); }

http://drakkos.co.uk

Phew, thats quite a lot, but it sets us up nicely for the code to follow. We've talked about all of this in the previous chapters, so go back and have another look over them if this doesn't all make sense. To begin with, lets set the scene for our players. We need to ensure they have a way of getting all information they need through interacting with the room. First of all, they need to know what the books are that need sorted. Lets tie that into an add_item. Because its going to be dynamic as to what the add_item should say, we need to use a function pointer (we'll talk about these at the end of this text). The skeleton for that would be as follows:
string books_to_sort(); void setup() { // Rest of setup as before. add_item ("book", (: books_to_sort() :)); }

307

Epitaph Online string books_to_sort() { return "blah"; }

http://drakkos.co.uk

Now, we need to build our books_to_sort() function. It should have the following behaviour: If there are books that have not been shelved, we should list those books If there are no book left to shelf, it should indicate that with a special message.

Lucky ducks that we are, we already wrote a method to do the bulk of this work for us query_unsorted_books. We just need to tie that into this method:
string books_to_sort() { string *unsorted = query_unsorted_books(); if (!sizeof (unsorted)) { return "All the books have been neatly sorted away."; } else { return "The following books are strewn around the library: " + query_multiple_short (unsorted); } }

So, thats step one giving the player the cue as to what remains to be done. The next step is to provide a way to find out whats been done so far specifically, what books are currently on which bookshelves. There are many ways we could do this: Allowing players to look at each bookshelf directly Looking at bookshelves gives the state of all books in all categories Looking at books individually tells which bookshelf they are on.

I favour a single add_item providing all information, because I feel it gives maximum usability with minimal frustration. So lets add a bookshelf item:
add_item (({"shelf", "bookshelf"}), (: books_on_shelves() :));

We also need a function for handling the text for when the player looks at the shelves. Our sorting mapping actually contains this information already, so we just need to process it and print it out 'real nice like':
string books_on_shelves() { string ret = ""; if (!sorting || !sizeof (sorting)) { return "No books have been sorted onto the shelves."; } else { foreach (string book, string category in sorting) { ret += "The book \"" + book + "\" has been put on the shelf for the category \"" + category + "\"\n";

308

Epitaph Online } return ret; } }

http://drakkos.co.uk

The final piece of information we need to provide is what categories are actually available. We could build that into the bookshelves, or provide a cue to look at another add_item. I favour the latter as its not information that is constantly updated and we risk overloading a player by bundling all the information into the same place. So we change our bookshelf add_item a little:
string books_on_shelves() { string ret = "The bookshelves are arranged according to categories. if (!sorting || !sizeof (sorting)) { ret += "No books have been sorted onto the shelves."; } else { foreach (string book, string category in sorting) { ret += "The book \"" + book + "\" has been put on the shelf for the category \"" + category + "\"\n"; } } return ret; } ";

And then we add in an item that gives us the categories:


string book_categories() { return "The following categories are labelled on the bookshelves: " + query_multiple_short (query_categories()); }

Now all that we need is a mechanism for allowing players to shift books from the main pile to a particular category, and for viewing the blurb on a book. A pair of add_commands would do nicely here, don't you think?

Guess the Syntax Quests


I am as guilty of this as anyone, but we must be mindful when coding an add_command to make it so that the syntax is obvious. Guess the syntax quests are frustrating for everyone, and usually revolve around a creator having picked an unusual word for a command (often without realizing it wasnt intuitive), and then providing no hints later as to what the right command actually is. We need to avoid these, but its not easy. One good way is to provide multiple command syntaxes that point to the same function, or just make sure you pick clear words from the start. Hints as to the right words to use should also be liberally sprinkled around your quest. For example, if we were to use the command sort, our book item might suggest that: 309

Epitaph Online string books_to_sort() { string *unsorted = query_unsorted_books();

http://drakkos.co.uk

if (!sizeof (unsorted)) { return "All the books have been neatly sorted away."; } else { return "The following books are strewn around the library: " + query_multiple_short (unsorted) + ". They are just begging to " "be given a good sorting."; } }

If you design a properly dynamic quest, you might even consider making the instructions available in a help-file, or on a web-page. The logical process you go through to solve the quest should be the task, not finding the actual commands you need to use.

Viewing the Blurb


Our books are not items. They could be, but theyre not players cant hold them, and they certainly cant open them up and read them. However, read is a command that will automatically be attempted if people want more information from the book, and so well provide a read blurb on book command. We should also give the hint to players that there are blurbs to read:
string books_to_sort() { string *unsorted = query_unsorted_books(); if (!sizeof (unsorted)) { return "All the books have been neatly sorted away."; } else { return "The following books are strewn around the library: " + query_multiple_short (unsorted) + ". They are just begging to " "be given a good sorting. Each has a blurb on the back that " "may help in discovering to what category they belong."; } }

So, first we add the command it goes into the init method:
void init() { ::init(); add_command ("read", "blurb on <string'book'>"); }

And then we add the code that handles the functionality. Its not complex, we already have methods to do everything we need to do:
int do_read (object *indirect_obs, string dir_match, string indir_match, mixed *args, string pattern) { string book = args[0];

310

Epitaph Online string blurb; blurb = query_book_blurb (book); if (!blurb) { add_failed_mess ("That book does not seem to exist.\n"); return 0; }

http://drakkos.co.uk

tell_object (this_player(), "The blurb on the book reads: " + blurb + "\n"); add_succeeded_mess ("$N pick$s up a book and read$s the back.\n", ({ })); return 1; }

With that, the blurb has been dealt with. Except, not entirely it doesnt take into account language skills or such. Well come back to that later.

The Sort Command


Now that we can read the blurb on the back of a book, lets give ourselves a sort command. As before, we put it in the init method of our room. Itll need to take in two pieces of information the book we want to sort, and the category into which we want to sort it:
void init() { ::init(); add_command ("sort", "<string'book'> into [category] <string'category'>"); add_command ("read", "blurb on <string'book'>"); } int do_sort (object *indirect_obs, string dir_match, string indir_match, mixed *args, string pattern) { string book = args[0]; string category = args[1]; return 1; }

So, when our player wants to take a book and sort it into a category, they type:
> sort book_name into category category_name

Or just:
sort book_name into category_name

The syntax message should hopefully be self-explanatory. No need to guess the syntax, its all there wrapped up in the command. Thats good practise a bad syntax is bad for everyone.

311

Epitaph Online

http://drakkos.co.uk

So, what do we need to do in our command? Well, we need to provide a way of giving useful feedback to the player for example, if they are trying to sort a book that doesnt exist, or into a category that isnt there, we should tell them that:
int do_sort (object *indirect_obs, string dir_match, string indir_match, mixed *args, string pattern) { string book = args[0]; string category = args[1]; if (find_book (book) == -1) { add_failed_mess ("That book does not seem to exist.\n", ({ })); return 0; } if (member_array (category, query_categories()) == -1) { add_failed_mess ("That category does not seem to exist.\n", ({ })); return 0; } return 1; }

Finally, if it turns out that they are trying to sort a valid book into a valid category, then let it be so:
int do_sort (object *indirect_obs, string dir_match, string indir_match, mixed *args, string pattern) { string book = args[0]; string category = args[1]; if (find_book (book) == -1) { add_failed_mess ("That book does not seem to exist.\n", ({ })); return 0; } if (member_array (category, query_categories()) == -1) { add_failed_mess ("That category does not seem to exist.\n", ({ })); return 0; } assign_book_to_shelf (book, category); add_succeeded_mess ("$N sort$s the book \"" + book + "\" into category \"" + category + "\"\n", ({ })); return 1; }

Finally, we need to add in a function that checks to see if the quest has been completed if our current library state matches the desired end-state (which is that all books are in the right category). Thats easy to do, since we have a pretty adaptable data structure in place. We need to check the following: If there are any unsorted books, then the quest is not complete. If there are any books in the mapping that dont exist in the database (which shouldnt happen, but its nice to make sure), we bail out. 312

Epitaph Online

http://drakkos.co.uk

If a book is shelved in an incorrect category (as in, one that it doesnt have in its category array), we bail out. If none of these things are true, the quest is complete and our player should be told so.

So, we translate that into a method:


int check_complete() { int i; if (sizeof (query_unsorted_books())) { return 0; } foreach (string book, string category in sorting) { i = find_book (book); if (i == -1) { return 0; } if (member_array (category, allBooks[i]->categories) == -1) { return 0; } } return 1; }

Finally, we hook this function into our sort command:


int do_sort (object *indirect_obs, string dir_match, string indir_match, mixed *args, string pattern) { string book = args[0]; string category = args[1]; if (check_complete()) { add_failed_mess ("The library is immaculately organised. "spoiling it now.\n", ({ })); return 0; } if (find_book (book) == -1) { add_failed_mess ("That book does not seem to exist.\n", ({ })); return 0; } if (member_array (category, query_categories()) == -1) { add_failed_mess ("That category does not seem to exist.\n", ({ })); return 0; } assign_book_to_shelf (book, category); if (check_complete()) { tell_object (this_player(), "If this were an active quest, you " "would have just gotten a wodge of XP!\n"); } add_succeeded_mess ("$N sort$s the book \"" + book + "\" into category \"" + category + "\"\n", ({ })); return 1; } Don't go "

313

Epitaph Online

http://drakkos.co.uk

And voila one quest, coded and ready to go. We still need to add in the quest accepting and hand-in framework, but we'll talk about that later when we discuss poor old Agnes.

Except, not quite...


Having gotten the core of the quest done, we need to do some fine-detail work to make it clear and simple to use. For one thing, we have a fairly awkward user interface problem:
> sort some stuff about you into action That book does not seem to exist. > sort Some stuff about you into action If this were an active quest, you would have just gotten a wodge of XP! You sort the book "Some stuff about you" into category "action"

Oh no! The reason for this is that we have capitalization in our book dataset, and so not only the letters must match, but the case of the letters must match too. We can resolve this by liberal use of lower_case and cap_words throughout our code. As a convention, it is always best to store string data in lower case (unless casing is important) and capitalize it as it is shown to the user. So when we add a book, we could add it like so:
void add_book(string book_title, string book_author) { // Code as usual book_title = lower_case (book_title); // Code as usual }

And then when the user enters the book and category to sort, we do the same:
string book = lower_case (args[0]); string category = lower_case (args[1]);

Now we have no need to concern ourselves about the capitalization of user input. Its all in the same format. However, it will look quite bad if we always output in lower case, so we can make sure that the user-facing representation gets a little bit of polish before it gets to the player. For example, in books_on_shelves():
else { foreach (string book, string category in sorting) { ret += "The book \"" + cap_words (book) + "\" has been put on the shelf for the category \"" + cap_words (category) + "\"\n"; } }

314

Epitaph Online

http://drakkos.co.uk

And in our do_sort:


add_succeeded_mess ("$N sort$s the book \"" + cap_words (book) + "\" into category \"" + cap_words (category) + "\"\n", ({ }));

There are some areas of our code where thats slightly tricky to do, such as when were working with query_multiple_short and arrays. We can easily write a method that handles that for us though:
string pretty_array_output (string *arr) { for (int i = 0; i < sizeof (arr); i++) { arr[i] = "\"" + cap_words (arr[i]) + "\""; } return query_multiple_short (arr); }

And then use it in place of each of the calls we have to query_multiple_short, like so:
string book_categories() { return "The following categories are labelled on the bookshelves: " + pretty_array_output (query_categories()); }

And:
string books_to_sort() { string *unsorted = query_unsorted_books(); if (!sizeof (unsorted)) { return "All the books have been neatly sorted away."; } else { return "The following books are strewn around the library: " + pretty_array_output (unsorted) + ". They are just begging to " "be given a good sorting. Each has a blurb on the back that " "may help in discovering to what category they belong."; } }

These kind of helper functions can greatly simplify the task of building a compelling user experience in your rooms, and also greatly increase the consistency of your interface. If we only occasionally use quotation marks, then we make our quest harder to do because people cant trust the visual cues. We dont want that either we do it everywhere, or we do it nowhere. In between is worse than not doing it at all. Now, back to our blurb. As it stands, it works but it doesnt work properly because we have a fairly complex language system on Epitaph. What happens if someone who has no knowledge of English tries to read our blurb? They shouldnt be able to do so. Luckily, our message system has a nice way of allowing you to turn any arbitrary bit of text into language specific text, like so:
$L$[read:english]This text will be unreadable to anyone who doesn't know

315

Epitaph Online English$L$

http://drakkos.co.uk

So therein lies our solution:


tell_object (this_player(), "The blurb on the book reads: " + "$L$[read:english]" + blurb + "$L$\n");

And then Bob is the brother of your mother or your father!

Conclusion
Thats a quest we just did. Its easily the most involved thing weve done in any of this material, but hopefully youll have seen it wasnt too bad, Indeed, its actually a good deal more complicated than many quests it has random elements that allow it to be entirely dynamic plus, the more books we add, the more variation can come into the quest. This isnt a quest room as it would necessarily appear in the game instead, its a starting point that you can play about with. There are many, many ways to make this quest better than it is. Really, the limit is only your imagination. This has been quite a complex chapter, and we havent yet touched on the output of this quest the secret code that feeds into the quests to follow. Well discuss that later. For now, lets bask in the warm glow of a hard job well done!

316

Epitaph Online

http://drakkos.co.uk

Quest Handlers
Introduction
The code that we have in place now describes the actual quest, but we still have to actually make the quest available. This requires us to do three things: Have the quest added to the quest handler Hook the code we have written into the player library Create the start and end point of the quest

In this chapter, we'll look at the process of setting up our code to interface with the two handlers dedicated to managing quests.

The Handlers
There are two handlers that exist to manage the complexity of quests. The first of these is the quest handler itself, which holds information about each of the quests such as what they are called, the hints associated with them, and the level. The second handler is the library, and the library holds the records of a specific player with regards to the quests they have done and any quest info that has been associated with them. The library is what we'll spend most of our time manipulating. Before we can do that, we need to include library.h to make available the library handler to our code. First, let's look at the one place in our code that currently has to handle the awarding of the quest:
if (check_complete()) { tell_object (this_player(), "If this were an active quest, you " "would have just gotten a wodge of XP!\n"); }

If we want to make this quest operational, we replace this with a call to set the appropriate player criteria in the library. When it comes time for the player to hand in the quest, then the quest handler will check the player's criteria value against the criteria value in the quest, and award (or refuse to award) accordingly. First we define the name of the quest (the same one that we had in our quest data file above) as QUEST_NAME, and then we call set_player_query_info on the library:
if (check_complete()) { LIBRARY->set_player_quest_info (this_player()->query_name(), QUEST_NAME, "organised library", 1); }

In cases where we want to award a quest immediately after it has been completed, 317

Epitaph Online

http://drakkos.co.uk

we can use the function set_quest to do that. This quest though, is done through a quest NPC and so we don't have to the quest architecture will handle it all for us. Now, let's talk about the last bit of functionality we need to add to our quest to make it fully functioning.

Cast Your Mind Back...


The last bit of the quest was that it was supposed to award the player with a secret code that worked on the secret door to come. That requires us to have some method of storing information on our player in a portable, easily accessible way. One method we have is the use of properties. We don't want to do that. Properties are useful, quick and easy but they tend to linger around and people forget what they are for. Unless there's a compelling reason to use a property, we should avoid it. One of the things that the library gives us is a mechanism for storing questrelated information along with players. This information gets stored as a mixed variable, and we have the responsibility in our code for working out what kind of data it should be, and parsing it appropriately. If all we're storing is a single piece of information, it's fine otherwise we need to put some code in around the edges. We'll come back to that later... We can set player information using the same set_player_quest_info method on the library. Our criteria will check this data, but we can use it store stuff that the handler won't check too. Lets generate a random code for a player, and have it stored along with the player's name:
string generate_random_code() { int rand = random (9000); rand += 1000; return "" + rand; }

And:
if (check_complete()) { LIBRARY->set_player_quest_info (this_player()->query_name(), QUEST_NAME, "organised library", 1); LIBRARY->set_player_quest_info (this_player()->query_name(), QUEST_NAME, "secret code", generate_random_code()); }

We can also pull information out with query_player_quest_info. Normally we'd store this information in the function rather than querying it from the library, but this is just proof of concept:

318

Epitaph Online

http://drakkos.co.uk

if (check_complete()) { LIBRARY->set_player_quest_info (this_player()->query_name(), QUEST_NAME, "organised library", 1); LIBRARY->set_player_quest_info (this_player()->query_name(), QUEST_NAME, "secret code", generate_random_code()); code = LIBRARY->query_player_quest_info (this_player()->query_name(), QUEST_NAME, "secret code"); tell_object (this_player(), "As you put away the last book, a small slip " "of aged paper flutters to the ground. You can see it has the " "numbers \"" + code + "\" on it before it crumbles into dust.\n"); }

Now you get a new piece of information when the quest completes:
As you put away the last book, a small slip of aged paper flutters to the ground. You can see it has the numbers "2023" on it before it crumbles into dust. You sort the book "Some Stuff About You" into category "Romance"

This is the information that we need for the later quest, now happily stored and ready for us to make use of later. This allows us to handle things like storing quest stage data for example, as a player progresses through a quest, an integer value can be stored to represent what state the quest is in for that specific player. Finally, we can put restrictions on participation with completed quests through the use of the query_quest_done method, like so, at the top of the sort method:
if (LIBRARY->query_quest_done (this_player()->query_name(), QUEST_NAME)) { add_failed_mess ("You have already done this quest. Give someone else a " "chance..\n", ({ })); return 0; }

The combination of these four methods will allow you to manage the functionality for multi-stage quests. The other option is to set formal pre-requisites on quests (which will stop them being available if you haven't met the pre-requisites), but either approach is fine.

Our Second Quest


Let's move on from this quest on to our second quest the partition wall that leads us on to the corridor of the school. Remember how we planned this quest out in Being A Better Creator. We have a dynamic setup that requires us to go through several steps: 1. Randomly generate a location for the partition wall. 2. If the player passes a perception task, show the wall when they look in the right place. 3. When a player looks at the wall, give them the clues as to how they need to break through it. 4. The player uses the hints given as to tool needed to break through the wall. 5. Upon doing so, the quest is granted and the player is moved into the 319

Epitaph Online

http://drakkos.co.uk

corridor beyond. So, now we need to first generate a random location for a wall. Let's place it behind a particular bookshelf. When the player looks at the categories or the bookshelves, we want to do a perception check and then generate a location for them to see the wall. We also want to generate a random tool to break through the roof. We'll store these as an array and then set the player's quest info appropriately. Looking for an appropriate task for this, we find TASKMAP_NOTICE_SOMETHING we'll make it a very easy check, but it's a check nonetheless.
string* check_wall_location (object player) { string category = LIBRARY->query_player_quest_info (player->query_name(), WALL_QUEST, "selected category"); string tool = LIBRARY->query_player_quest_info (player->query_name(), WALL_QUEST, "selected tool"); string *cat = ({"romance", "action", "childrens"}); string *tools = ({"crowbar", "hammer"}); int result; if (tool && category) { return ({ category, tool }); } result = TASK_MAPPER->perform_task (player, TASKMAP_NOTICE_SOMETHING, TTSM_DIFF_INCREDIBLY_EASY, 0, TM_FREE); switch(result) { case AWARD : taskmaster_mess ( player, ({"You feel a little more perceptive."})); case SUCCEED : category = element_of (cat); tool = element_of (tools); LIBRARY->set_player_quest_info (player->query_name(), WALL_QUEST, "selected category", category); LIBRARY->set_player_quest_info (player->query_name(), WALL_QUEST, "selected tool", tool); return ({ category, tool }); break; default : return ({ }); } }

We hook this into our shelf add_item so that people looking at the add_item have a chance of seeing the wall:
string books_on_shelves() { string ret = "The bookshelves are arranged according to categories. string *details; details = check_wall_location (this_player()); if (!sorting || !sizeof (sorting)) { ret += "No books have been sorted onto the shelves."; }

";

320

Epitaph Online else { foreach (string book, string category in sorting) { ret += "The book \"" + cap_words (book) + "\" has been put on the shelf for the category \"" + cap_words (category) + "\"\n"; } }

http://drakkos.co.uk

if (sizeof (details)) { ret += " You can see a crack in the roof above the \"" + details[0] + "\" bookshelf. If you had a " + details[1] + ", you could probably break through the roof."; } return ret; }

Lovely, huh? Now we need an add_command that lets the player break the wall:
add_command ("break", "wall behind <string'category'> bookshelf");

And then the code to handle it:


int do_break (object *indirect_obs, string dir_match, string indir_match, mixed *args, string pattern) { string string string object category = args[0]; *details; location, tool; *valid_tools;

if (LIBRARY->query_quest_done (this_player()->query_name(), HOLE_QUEST)) { add_failed_mess ("You have already broken through the wall, no need " "to do it again.\n", ({ })); return 0; } details = check_wall_location (this_player()); if (!sizeof (details)) { add_failed_mess ("Break what? Why? "being such a vandal.\n", ({ })); return 0; } location = details[0]; tool = details[1]; if (category != location) { add_failed_mess ("The wall there is too solid to break.\n", ({ })); return 0; } valid_tools = match_objects_for_existence (tool, this_player(), this_player()); if (!sizeof (valid_tools)) { add_failed_mess ("You have no tool suitable for breaking through " "the wall.\n", ({ })); return 0; } You are a bad person for "

321

Epitaph Online

http://drakkos.co.uk

LIBRARY->set_player_quest_info (player->query_name(), WALL_QUEST, "broken wall", 1); add_succeeded_mess ("$N break$s through the wall with $I.\n", ({ valid_tools[0] })); return 1; }

Our final step is then adding the command that lets the player clamber through the wall. So that they don't need to keep breaking it, we'll make it conditional on them having actually completed the quest previously. However, since we're not actually completing these quests (they are set to inactive so they don't show up in player scores), we need to comment that bit out to test it. And there we have it one room, two quests it's tremendous value for money by any standard!

Conclusion
We've made a lot of progress in this chapter we turned our quest architecture into an actual quest, and then added in the second of our three quests. Okay, so the second one isn't especially interesting, but it's there! We have one more quest to add to our development, and then we have our three. Then there are all sorts of other exciting things we need to include in our village. Are you excited? I'm excited touch your nose if you're excited too!

322

Epitaph Online

http://drakkos.co.uk

Our Last Quest


Introduction
Two quests behind us we're burning through our list of features here! Now we add in the third, and then we can devote our attention to the other parts of the village we are currently ignoring. Our last quest as I am sure you can recall from Being A Better Creator is to find an electronic keypad behind a painting, and then enter the secret code that we were given to open a the door to the basement. The clues that we give to this quest should be provided by the sorted library below we should be able to research' details about the portraits and the hints that we get reveal the answer to the puzzle. That's the topic for this chapter researching in the library, and the mechanisms of pushing portraits.

The Quest Design


The corridor of our school is going to be a portrait gallery, and behind one of these portraits is an electronic keypad. Finding the portrait will be the quest, rather than simply entering the code after all, that's just ferrying a number from one place to the other. The puzzle we build then should be logical, possible to solve by diligent effort, and that effort should be a worthwhile shortcut to simply brute-forcing the quest by pushing aside every single portrait. Our design then must accommodate this. First, let's think about how to handle the brute-forcing bit. We have two obvious courses: Thousands of portraits A limited number of guesses

Thousands of portraits could be generated fairly easily by a function, but it seems like a pretty awkward solution. On the other hand, if we make it so they can push aside perhaps three portraits in twenty before the state of the puzzle resets, then it's important that those three portraits are the right, or sensible portraits. Let's consider what these portraits might actually be it's a rural school, so they are likely to be pictures of past headmasters and headmistresses, and perhaps well-known alumni. It's pretty easy to algorithmically generate twenty or so suitably headmastery names. Likewise, we can decide on some features that define each one size of head, colour of hair, height, weight, eye-colour, attractiveness, and so on. Then, we can provide a set of clues that uniquely identify one specific portrait as being worth moving. We could hard-code each of these portraits, but we like dynamic quests here on Epitaph, so we'll do them randomly.

323

Epitaph Online

http://drakkos.co.uk

Anyway, enough of that though, we are eager for action! But before we begin coding, let's talk about a very Epitaphy way of dealing with distributed data access.

Handlers
We use the word handler' a lot on Epitaph. For new creators, they often conjure up mental images of horribly complicated and important code, locked away in mysterious mudlib directories and not for casual perusal. While that's true of a number of our handlers, the basic concept is very simple. In our scenario above, we are faced with the task of co-ordinating some functionality across two separate rooms. We must be able to research people in the library, and the people we research must be represented in portrait form in the corridor. We need a way of storing data so that these two objects can have access to it. One way to do this is to have one room call functions in another room. Unfortunately, due to the way rooms work on Epitaph, this is not a reliable strategy. Rooms unload themselves when no-one is in them so that we reduce memory load. When a function gets called on an unloaded room, it forces the room to reload along with any setup that gets done. So if we generate twenty random portraits, then simply moving from one room to another may be enough to change the entire state of the quest for a player. They go from a room containing one set of portraits to a library referring to another set. That's not good. Instead, what we use is a handler a handler is just a normal object that is designed to be accessed by multiple other objects. It defines a set of data and stores it in a single place, and it provides functions for acting on that data. Henceforth, no room has to store this data because they just make calls on the handler. Really, the kind of handlers that are associated with small area developments are mini versions of the more substantial handlers that exist over entire domains and in /mudlib/handlers. Nonetheless, the concept remains the same. There is often some confusion as to the distinction between a handler and an inherit they do after all seem to occupy similar roles in coding. The difference lies in the persistence of data in an inherit, each object gets its own copy of the data, and if that data is randomly set up, it'll have a different set of data from all the other objects. In a handler, there is only one set of data and all objects make use of that single set. In terms of what inherits allow you to do with shared functionality, they are almost identical except that handlers are easier to add in after the fact. Our taskmapper is a handler. There is no reason why things like perform_task couldn't be provided as a function as part of STD_OBJECT. However, if you were writing a piece of code that didn't inherit STD_OBJECT, then you'd need to bind in 324

Epitaph Online

http://drakkos.co.uk

the inherit yourself. Additionally, if you wanted to change the way perform_task worked, you'd need to force a reboot of the MUD otherwise you couldn't be sure which version of the perform_task code any given object would be using at any time. In essence, you use an inherit when all you care about is a set of objects having access to some common set of functionality. You use a handler when you want a set of objects having access to some common set of data. For our purposes, we need a handler.

The Portrait Handler


Our handler is not complicated. It doesn't inherit from anything because it's just an object for storing some data and functionality. We'll put it in a new subdirectory of Zombieville, and include a new define in our path.h:
#define #define #define HANDLERS INHERITS ROOMS ZOMBIEVILLE + "handlers/" ZOMBIEVILLE + "inherits/" ZOMBIEVILLE + "rooms/"

Our handler just needs a create method much like the first look we had at the code for the library:
void create() { seteuid (getuid()); }

Now, we build a data representation. Since we've already done some work with classes, let's make a class for a portrait:
class portrait { string name; string title; string eyes; string height; string weight; string looks; string hair; } class portrait *all_portraits; void create() { seteuid (getuid()); all_portraits = ({ }); }

Now we need something to randomly generate each of the different elements of the portrait. A real quest would be more imaginative in the names we apply here, but never mind. First, something to generate a random name:
string random_portrait_name() { string *forename = ({"albert", "eric", "alison", "agnes", "dumbledore", "wanda", "derek", "doris", "marta", "pauline"});

325

Epitaph Online

http://drakkos.co.uk

string *surname = ({"mactavish", "mckay", "king", "wilson", "gregor", "stuart"}); return element_of(forename) + " " + element_of(surname); }

And something to generate a random title:


string random_portrait_title() { string *titles= ({ "former patron of the school", "former head of the school", "former almunus of the school", }); return "the " + element_of (titles); }

The rest we can generate a little more easily. However, we need the name of the wizard to be a unique identifier how else will we be able to allow players to research them? So we first need a way to find a specific wizard's portrait. We've already seen this in action:
int find_portrait (string name) { for (int i = 0; i < sizeof (all_portraits); i++) { if (all_portraits[i]->name == name) { return i; } } return -1; }

Now, let's make a method that creates an entirely random portrait:


void add_portrait() { class portrait new_portrait; string wname; string wtitle; string *eyes_arr = ({"blue", "green", "black", "red"}); string *height_arr = ({"tall", "short", "medium-height"}); string *weight_arr = ({"fat", "skinny", "stocky", "plump"}); string *looks_arr = ({"handsome", "ugly", "grotesque", "beautiful"}); string *hair_arr = ({"black", "blonde", "grey", "white", "brown"}); do { wname = random_portrait_name(); } while (find_portrait (wname) != -1); wtitle = random_portrait_title(); new_portrait = new (class portrait, name: wname, title: wtitle, eyes: element_of (eyes_arr), height: element_of (height_arr), weight: element_of (weight_arr), looks: element_of (looks_arr), hair: element_of (hair_arr)); all_portraits += ({ new_portrait });

326

Epitaph Online }

http://drakkos.co.uk

Then we add a method that gives us the string description of a portrait (what we get when we look at it). We're going to use an efun called sprintf here sprintf lets us perform some sophisticated string parsing without us having to play around with concatenation and such. It works very similarly to a pattern in an add_command, except we use a %s to represent a string and we pass the bits to be filled in as parameters to the function, like so:
string portrait_long (string person) { int i = find_portrait (person); if ( i == -1) { return 0; } return sprintf ("This is a portrait of %s, %s. The portrait is of a a %s, %s person, " "with %s hair, %s eyes and a %s face.\n", cap_words (all_portraits[i]->name), all_portraits[i]->title, all_portraits[i]->height, all_portraits[i]->weight, all_portraits[i]->hair, all_portraits[i]->eyes, all_portraits[i]->looks); }

When we call this function with a wizard represented as a portrait, we get something like the following back out:
This is a portrait of Pauline Wilson, the former patron of the school. The portrait is of a a medium-height, skinny person, with brown hair, blue eyes and a ugly face.

Now we just need a mechanism to identify which of these portraits is the one behind which the secret panel may be found. Exactly how we'd choose to do this in a real quest for the game doesn't matter we don't want to lock great quest ideas away in a tutorial! Aside from the library quest (which would be serviceable as an actual quest for the game with some modification), these quests are illustrations of concept rather than quests we'd expect people to enjoy. What we're going to do next is illustrate why classes are such a good way of representing data. We need to add in a new element of the class a history' for people to find when they research the wizard. We'll assign these semi-randomly by means of a parameter passed to the add_portrait method: Step one is to add two new elements to the class:
class portrait { string name; string title; string eyes; string height; string weight; string looks; string hair; string history; int secret_panel;

327

Epitaph Online }

http://drakkos.co.uk

And then a modification to add_portrait:


void add_portrait(int suspected) { class portrait new_portrait; string wname; string wtitle; string *eyes_arr = ({"blue", "green", "black", "red"}); string *height_arr = ({"tall", "short", "medium-height"}); string *weight_arr = ({"fat", "skinny", "stocky", "plump"}); string *looks_arr = ({"handsome", "ugly", "grotesque", "beautiful"}); string *hair_arr = ({"black", "blonde", "grey", "white", "brown"}); string *banal_history = ({ "this person donated lots of money to the school.", "this person was a school sport's day winner.", "this person dramatically improved school dinners.", "this person was made, almost entirely, from cheese." }); string *suspect_history = ({ "this person was known for hiding things behind other things.", "this person had a gift for secret, sleekit things.", "this person was a dirty, sneaky little spy.", }); string hist; if (suspected) { hist = element_of (suspect_history); } else { hist = element_of (banal_history); } do { wname = random_portrait_name(); } while (find_portrait (wname) != -1); wtitle = random_portrait_title(); new_portrait = new (class portrait, name: wname, title: wtitle, eyes: element_of (eyes_arr), height: element_of (height_arr), weight: element_of (weight_arr), looks: element_of (looks_arr), hair: element_of (hair_arr), history: hist, secret_panel: suspected ); all_portraits += ({ new_portrait }); }

Now, if we pass in a positive number as a parameter, we'll get one of the suspect' histories. We'll make it so that a secret_panel value of 1 means that the person might be where the panel is, and 2 means that's where it actually is. Next, we 328

Epitaph Online

http://drakkos.co.uk

need a method to setup the state of the handler:


void setup_data() { int location = random (10); int panel; all_portraits = ({ }); for (int i = 0; i < 10; i++) { panel = 0; if (i == location) { panel = 2; } else if (i % 3 == 0) { panel = 1; } add_portrait (panel); } }

So here we generate where the panel is going to be, and we make every third portrait a suspect' one. Now, although we can query the handler as to which portrait is which, every time the method is called we get a brand new set of data to work with. Before we're done here though, we need to add in a few more utility functions:
string query_portrait_history (string wizard) { int i = find_portrait (wizard); if ( i == -1) { return 0; } return all_portraits[i]->history; } int query_portrait_panel (string wizard) { int i = find_portrait (wizard); if ( i == -1) { return 0; } return all_portraits[i]->secret_panel; } string* query_portraits() { string *ret = ({ }); foreach (class portrait p in all_portraits) { ret += ({ p->name }); } return ret; }

And that's pretty much the code we need for our portrait handler, give or take 329

Epitaph Online

http://drakkos.co.uk

some light ornamentation and perhaps some elegant wainscoting.

Back to the Library


So, we're not done with our library yet. Now we're going to add yet another piece of functionality the ability to research' things. Some of these things will be related to other parts of the village, some should be related to other players (as per our village design in Being A Better Creator), but we also want people to be able to research wizards to see what they are known for. The process for doing this should be familiar now we create an add_command. However, instead of making use of functions in our room, we're going to make calls on the handler we just created. Handlers are usually #defined in a header file somewhere. We have a define for our handlers directory, but we also want one for the handler we created itself:
#define #define #define #define HANDLERS INHERITS ROOMS PORTRAIT_HANDLER ZOMBIEVILLE + "handlers/" ZOMBIEVILLE + "inherits/" ZOMBIEVILLE + "rooms/" (HANDLERS + "portrait_handler")

Notice here that we surround the path in brackets, which we haven't done before. The reason for this is the pre-processor that does all the clever replacing of our defines in our code. We'll come back to this. We need an add_command as usual;
add_command ("research", "<string>");

And the code to handle it:


int do_research (object *indirect_obs, string dir_match, string indir_match, mixed *args, string pattern) { string history; string topic; topic = args[0]; if (check_complete()) { add_failed_mess ("The library is too disorganised for you to be able " "to find any useful information.\n", ({ })); return 0; } history = PORTRAIT_HANDLER->query_portrait_history (topic); if (!history) { add_failed_mess ("The library doesn't seem to have any information on " "that topic.\n", ({ })); return 0; }

330

Epitaph Online

http://drakkos.co.uk

tell_object (this_player(), "As far as you can tell, " + history + "\n"); add_succeeded_mess ("$N flick$s through some of the books " "in the library.\n", ({ })); return 1; }

Note how we make our call on the portrait handler in exactly the way we've made calls on the taskmapperin the past. This is why we need to use the brackets in our define. The pre-processor does the search and replace on the code we write, putting in the correct values for each of our defines. If we miss out the brackets, this is what the code looks like to the compiler:
history = "/d/support/learning/zombieville/" + "handlers/" + "portrait_handler"->query_portrait_history (topic);

Due to the way the MUD evaluates these operators, it attempts to call the function on the object portrait_handler", rather than the fully qualified name. With the brackets, the code looks like this:
history = ("/d/support/learning/zombieville/" + "handlers/" + "portrait_handler")->query_portrait_history (topic);

The brackets tell the driver Hey, put all this together before you attempt to call that function', pretty much exactly in the way you probably remember from your primary school maths classes. This ensures the function gets called on the fully qualified "/d/support/learning/zombieville/handlers/portrait_handler", which is an actual object it knows how to find.

Conclusion
We only have the corridor of the school to do now to make this quest work, and that follows on naturally from what we've been discussing in this chapter. We've got a handler now, and being able to bundle all the functionality into that greatly simplifies the task of implementing the rest of this quest. Handlers are a common and powerful approach to development on Epitaph, and you'll find that you will have cause to make use of them often if developing code a little beyond the ordinary. They are nothing to be feared they are identical to almost every other piece of code you have seen, and you are very much capable at this point of developing your own, as we have just done.

331

Epitaph Online

http://drakkos.co.uk

332

Epitaph Online

http://drakkos.co.uk

Finishing Touches
Introduction
And now we round off our fairly lengthy discussion of the Zombieville quests with the last part of the puzzle the main corridor of the school. In this room we tie together all of our quests into a neat, integrated whole. We've already got a portrait handler, so now all we need to do is provide the mechanism for moving portraits aside and entering the secret code. Come, take my hand I have such wonders to show you.

The Second Level Room


Okay, let's start off with the room itself. It's a simple, humble room but it's ours. We begin with a skeleton:
#include "path.h" #include <library.h> inherit INHERITS + "inside_room"; void setup() { set_short ("main corridor through the school"); add_property ("determinate", "the "); set_long ("This is the main corridor that makes its way through the " "school. It is lined with potraits of dull people..\n"); set_light (100); add_exit ("east", ROOMS + "library", "path"); }

And there's a pretty familiar pattern we follow. First, we add in some add_items that let us represent the view of the data from our handler. One for portraits to begin with, so that our players can see the portraits available for them to view:
string portrait_long() { string *portraits = PORTRAIT_HANDLER->query_portraits(); return "There are many portraits hanging around the room. You can see " "portraits for the following people: " + query_multiple_short (portraits) + ". Undoubtedly more information about each of them " "could be researched in the library.\n"; }

Now when we look at the portraits, we'll see something like this:
There are many portraits hanging around the room. You can see portraits for the following people: pauline king, doris gregor, derek gregor, albert mckay, doris wilson, marta mactavish, marta king, dumbledore mactavish, dumbledore wilson and albert stuart. Undoubtedly more information about each of them could be researched in the library.

Unfortunately, we need to do a little bit of processing so that it actually looks 333

Epitaph Online

http://drakkos.co.uk

good capitalising each of the names. There are ways we can do this quickly and easily, but we'll have to do it longhand for now:
string portrait_long() { string *portraits = PORTRAIT_HANDLER->query_portraits(); for (int i = 0; i < sizeof (portraits); i++) { portraits[i] = cap_words (portraits[i]); } return "There are many portraits hanging around the room. You can see " "portraits for the following wizards: " + query_multiple_short (portraits) + ". Undoubtedly more information about each of " "them could be researched in the library.\n"; }

Now we see something a lot easier on the eye:


There are many portraits hanging around the room. You can see portraits for the following wizards: Pauline King, Doris Gregor, Derek Gregor, Albert Mckay, Doris Wilson, Marta Mactavish, Marta King, Dumbledore Mactavish, Dumbledore Wilson and Albert Stuart. Undoubtedly more information about each of them could be researched in the library.

We've already handled the research part, so now we just need to add in the mechanism for handling the quest. Remember, we've decided upon there being a limit to the number of guesses a player can make regarding the portraits they work with let's set it at three guesses. Each wrong guess will result in a clue to the player, and a correct guess will reveal the secret pad for them to enter the number.

I'm Looking Through... uh... behind you


Looking behind a portrait is a pretty straight-forward affair an add_command does the business as ever:
add_command ("look", "behind portrait of <string>");

And then we need the function to handle it. Remember that we decided that the secret_panel value of the portrait would define if it was where the number-pad could be found:
int do_look (object *indirect_obs, string dir_match, string indir_match, mixed *args, string pattern) { string head; int panel; int h; head = lower_case (args[0]); h = PORTRAIT_HANDLER->find_portrait (head); if (h == -1) { add_failed_mess ("You can't find a portrait of that person...\n",

334

Epitaph Online ({ })); return 0; } panel = PORTRAIT_HANDLER->query_portrait_panel (head);

http://drakkos.co.uk

if (panel == 2) { tell_object (this_player(), "There is a number-pad behind the " "portrait of " + cap_words (head) + "! It's just waiting " "for you to enter a number.\n"); } add_succeeded_mess ("$N move$s aside a picture and look$s behind it.\n", ({ })); return 1; }

Now we need to decide how to handle the finding of the keypad. We could create an actual keypad object that gets cloned into the room when a player finds it that would work. Or, we could simply update the player's quest info with the appropriate stage of the quest. Either of these is a workable solution, but we're going to go for the latter because it removes a number of complications (for example, should a player that wanders into a room after another player be able to see the secret number pad?). We'll make entering the code handled by another add_command, and make the entrance criteria of that command linked to the quest stage the player is at. Simple! This quest in the quest handler is stored as art investigator', so that's the quest we're going to define. Remember too that we need access to the quest info for visiting librarian' in order to determine if the code entered is correct . We define our door quest as DOOR_QUEST, and incorporate an update of the quest info in the success:
if (panel == 2) { tell_object (this_player(), "There is a number-pad behind the " "portrait of " + cap_words (head) + "! It's just waiting " "for you to enter a number.\n"); LIBRARY->set_player_quest_info (this_player()->query_name(), DOOR_QUEST, "found pad", 1); }

That's the portrait side handled most of the work is done in the handler, so we just need to provide methods to query the various elements. Next, we move on to handling the secret code.

Ecretsay Odecay
To begin with, the add_command:
add_command ("enter", "code <string>");

And then the functions to handle it: 335

Epitaph Online

http://drakkos.co.uk

int do_enter (object *indirect_obs, string dir_match, string indir_match, mixed *args, string pattern) { string entered_code = args[0]; string needed_code; if (!LIBRARY->query_quest_done (this_player()->query_name(), DOOR_QUEST)) { add_failed_mess ("Enter what into what? ({ })); return 0; } needed_code = LIBRARY->query_player_quest_info (this_player()->query_name(), LIBRARY_QUEST, "secret code"); if (entered_code != needed_code) { add_succeeded_mess ("$N enter$s a code on a secret number pad, but " "nothing happens.\n", ({ })); return 1; } add_succeeded_mess ("$N enter$s a code on a secret number pad.\n", ({ })); call_out ("transport_player", 0, this_player()); return 1; } You're crazy, dude.\n",

Note the use of the call_out in the do_enter method unfortunately, the nature of message parsing on the MUD means that it's sometimes difficult to enforce the proper order in which we want messages to be printed. The call_out ensures that our function has time to complete before our new messages get shown.

Random Guessing
We noted in an earlier chapter that we didn't want players to simply look behind every single portrait in order to find the secret pad we want them to have a limited number of guesses. That's pretty easy to handle in the room, we hold a mapping that stores how many guesses a player has had. We can clear it every time reset is called:
mapping guesses; void reset() { ::reset(); guesses = ([ ]); }

Then in the do_look method, we incorporate the code for handling the number of guesses along with a message indicating how close the player has come to using 336

Epitaph Online

http://drakkos.co.uk

up their last chance. Of course, these messages are just proof of concept real quests don't have quite such feeble justification. Or at least, they shouldn't!
num_guesses = guesses[this_player()->query_name()]; num_guesses += 1; guesses[this_player()->query_name()] = num_guesses; if (num_guesses > 3) { add_failed_mess ("None of the portraits will move for you! ({ })); return 0; } How odd.\n",

switch (num_guesses) { case 1: tell_room (this_object(), "The painting moves easily, but there is " "an rumble of creaky machinery that appears and then fades away...\n"); break; case 2: tell_room (this_object(), "It is hard to move the portrait - as if " "someone was holding on from the other side of the frame. Odd.\n"); break; case 3: tell_room (this_object(), "It is almost impossible to move the " "portrait. It's like it has suddenly become mechanically glued to " "the wall!\n"); break; }

Now each player gets a maximum of three attempts to find the secret panel per reset. If we want to be Complete Bastards, we can even make it so that the entire state of the quest is set up anew every reset:
void reset() { ::reset(); guesses = ([ ]); PORTRAIT_HANDLER->setup_data(); tell_room (this_object(), "There is a grinding of machinery, and all the " "portraits in the room are shuffled around as if they were a deck of " "cards being shuffled by Escher.\n"); }

And that's it all three quests in place. They are crying out for polishing and prettying up, but the process of building the quests is complete. There is always more we can do, and interested creators are advised to spend time looking at how they can adapt these quests to be better and more interesting it's always a valuable lesson to expand on a framework that is already provided.

What Kind Of Day Has It Been?


So, what have we learned over the past few chapters? Well, quite a lot! However, what we haven't learned really is to write quests. What we've learned is how to 337

Epitaph Online

http://drakkos.co.uk

write these specific quests, and use the tools that are available to build a back-end architecture and a user front-end. Every quest is different though like delicate little snow-flakes. For each one, you'll need to go through the same process, but the results will always be different: Choose a data representation Implement methods for managing that representation Put in place a user interface for those methods

The important thing to take away from these last chapters is the technical tools we have been discussing: classes inheritables add_commands handlers the quest library

These will serve you in good stead for putting together the back-end of any quest you can imagine. The limit is really your imagination. The quests that we have put in place have shown us examples of all of these tools being used in concert to achieve a fairly complex end.

Conclusion
Three quests that's not to be sniffed it. Fair enough, they're not particularly good quests, and lack a certain panache but we'd be doing ourselves a disservice if we hid all our best ideas in a set of tutorial documents. Nonetheless, what we have are three operational quests involving dynamic quest design and a consistent narrative. Understanding how these three quests are put together is the key to developing your own original and much more interesting developments. Now that we have finished with the library (for now), we'll move on to the other parts of the village. We've still got a boss to write, some NPCs, and a shop full of lovely tartan merchandise for the fashion conscious survivor. Our village demands our attention, we must answer its siren call!

338

Epitaph Online

http://drakkos.co.uk

Zombies
Introduction
We've been through some pretty intense material in the past few chapters, so let's calm the pace a little by talking about our cannon fodder the zombies who make up the majority of the wandering population of Zombieville. They are poor souls, caught by the infection and turned into ravenous, murderous ghouls looking to eat your tiny brain as an hors d'oeurve. We know how to develop NPCs we did that several times as part of Introductory LPC 1. In this chapter we're going to extend the concept a little to discuss some of the additional functionality built into NPC objects.

The Zombie Template


Because our zombies are the only wandering NPCs in the village, we want them to look as different as possible. As such, we won't develop NPCs with static descriptions they'll be dynamically generated to ensure variety. So, we create a chars directory in Zombieville and add it to our path.h:
#define #define #define #define #define HANDLERS INHERITS ROOMS CHARS PORTRAIT_HANDLER ZOMBIEVILLE ZOMBIEVILLE ZOMBIEVILLE ZOMBIEVILLE + + + + "handlers/" "inherits/" "rooms/" "chars/"

(HANDLERS + "portrait_handler")

In our zombie NPCs, we're going to make use of a lot of things we've discussed in previous chapters to add the dynamics. It's not a complex system, but it's very flexible. Let's start off simply with the basic framework. Let's create an NPC inherit, just because we can:
inherit STD_ZOMBIE; void create(){ do_setup++; ::create(); do_setup--; if ( !do_setup ){ this_object()->setup(); this_object()->reset(); } }

We'll use this inherit for all our zombie NPCs, just in case we want to add any common functionality to the various inhabitants of the area. Notice that we inherit STD_ZOMBIE here - as you should expect by now, all our neat zombie 339

Epitaph Online

http://drakkos.co.uk

related functionality is stored in here. Setting up the NPC involves us making use of some randomised elements we want to set the long description based on the type of zombie it is, which requires us to first set up an array of choices and then select one of those at random:
string *types = ({"spinstress", "schoolchild", "farmer"});

And:
string selected = element_of (types);

Now, this will work just dandy - but we have anouther choice as well. Maybe we don't want there to be an even distribution of these - maybe we want them to appear in different proportions. There are various ways to accomplish this, but one way is to use what is known as a roulette wheel. Basically this is a mapping that defines weights of options, which we then pass into an efun called, appropriately enough, roulette_wheel:
mapping types = ([ "spinster" : 1, "schoolchild" : 5, "farmer" : 3 ]); string type; type = roulette_wheel (types);

When you use the roulette_wheel sfun, it's just like spinning a real roulette wheel except all the compartments are differently sized - for our wheel above, we'll get a spinster once out of every nine times (1 + 5 + 3), a schoolchild five times out of nine, and a farmer three times out of nine. Either of these ways are appropriate for setting up the internals, but we're going to use the roulette wheel just because we haven't before. Having done this, we can actually setup the various parts of our NPC: #include "path.h"
#include <npc.h> inherit INHERITS + "zombie"; void setup() { mapping types = ([ "spinster" : 1, "schoolchild" : 5, "farmer" : 3 ]); string type; string *ret = ({ }); type = roulette_wheel (types); set_name (type);

340

Epitaph Online

http://drakkos.co.uk

switch (type) { case "spinster": ret += ({ "This is a poor old lady who never got married, and then " "she became a zombie." }); set_gender (2); basic_setup ("zombie", "standard", MONSTER_LEVEL_WEAK); break; case "schoolchild": if (random(2) == 0) { ret += ({ "This is a schoolboy, who became a zombie. It's not " "the dog that ate his homework, it's the boy who ate his " "teacher." }); set_gender (1); } else { ret += ({ "This used to be a schoolgirl. Now it's just a " "flesh-hungry, psychotic automaton." }); set_gender (2); } basic_setup ("zombie", "standard", MONSTER_LEVEL_MINION); break; case "farmer": ret += ({"This is a farmer, or at least, someone who used to be. " "Now the only thing this ghoul is farming is the living."}); set_gender (1 + random (2)); basic_setup ("zombie", "standard", MONSTER_LEVEL_NORMAL); break; } set_long (implode (ret, " } ") + "\n");

Notice that we take advantage of the flexibility of this kind of system to set up each kind of zombie slightly differently - schoolchildren are minions, spinsters are weak, and farmers are of normal strength. Note too the way that we build our long description too - by creating an array and then imploding it in set_long. Next, we can make use of the same system that we used for the portraits to describe each of the girls. First we declare arrays to hold all the choices:
string string string string *lengths = ({"long", "short"}); *colours = ({"blonde", "brunette", "red", "black"}); *weight = ({"fat", "thin", "skeletal", "plump", "well-built"}); *height = ({"tall", "short"});

We've got two ways in which we can handle the building of the rest of the long description. If we have something different we want to say for each options, we can do that through the use of sequential switch statements:
switch (element_of (height)) { case "tall": ret += ({"The zombie is unusually tall, which adds an extra dimension " "of fear to its shambling approach."}); break; case "short": ret += ({"The zombie is unusually short, meaning its gnashing jaws are " "even closer to bits of your body you wouldn't generally want " "gnawed."}); break;

341

Epitaph Online }

http://drakkos.co.uk

On the other hand, we can also handle it by simply slotting the selected values into a preset string - that's a lot quicker, but loses us the opportunity to have specific descriptions for each aspect. They are both perfectly appropriate ways of accomplishing this goal, so you should be motivated by how much you think you can benefit from writing out each options. Let's look at how we could accomplish it the second way. We are going to handle the building of the descriptive string a little differently here We're going to create our own control codes and use the replace efun on the description:
ret += ({"$C$$pronoun$ is a $height$, $weight$ zombie with $length$, $colour$ hair."}); long_desc = implode (ret, " ");

long_desc = replace (long_desc, ({ "$height$", element_of (height), "$weight$", element_of (weight), "$length$", element_of (lengths), "$colour$", element_of (colours), "$pronoun$", this_object()->query_pronoun(), })); set_long (long_desc + "\n");

Note we don't have a case here for $C$ - that's because it's a general code useable in any string, and it means 'capitalise the next letter'. By using the replace efun, we can thread these codes easily throughout a whole long description - the code will be changed into the appropriate value when the string is constructed, and what we end up with is something like this:
This is a poor old lady who never got married, and then she became a zombie. She is a tall, skeletal zombie with long, red hair. The left cheek of the spinster has been chewed away at some point, exposing grey, chipped bone to the world. The neck the spinster is cocked at an awkward angle, and visible bits of bone jut out of the greying flesh.

Finally, we need to set up our chats - zombies aren't great conversationalists, so we don't need to worry too much about this, just add some generic moans and groans:
load_chat(10, ({ 1, "@moan", 1, "@groan", 1, "' Braaaaaaaains", 1, "' Kiiiiiddddnneeeeeeeysss", 1, ": staggers.", })); load_a_chat(10, ({ 1, "@moan angrily", 1, "@groan angrily", }));

342

Epitaph Online

http://drakkos.co.uk

We can dress them as usual through the NPC clother - but if you want to create up some unique outfits for them, you should consider that as a reader exercise.

Event Driven Programming


One of the things that the MUD does at pre-set intervals is generate events. These are functions that get called on objects when certain things have happened in the game. We can provide functions that catch these events and execute code when the events occur. This is something easier to see in practise than it is to describe in theory, so let's look at a specific example. One of the times that the MUD triggers an event is when an NPC or a player dies. A function gets called on the NPC itself, as well as all of the objects in the environment of the NPC. The function that gets called is event_death, and it comes with a number of parameters:
void event_death(object ob, object *killers, object killer, string room_mess, string killer_mess) { ::event_death (ob, killers, killer, room_mess, killer_mess); }

The first parameter is the object that died. Killers is the array of objects that made up the dead NPC's attacker list. Killer is the object that struck the killing blow, and room_mess and killer_mess are the strings that get displayed to the room and killer respectively. So, let's make our NPCs a little more responsive to the world around them, and express their bloodlust when they see someone killed, and react with excitement if they were the one to do it.
void event_death(object ob, object *killers, object killer, string room_mess, string killer_mess) { if (member_array (this_object(), killers) != -1) { init_command (": snarls in triumph!", 1); } else if (ob != this_object()) { init_command (": looks longingly at the fresh meat that has arrived in " "its vicinity.", 1); } ::event_death (ob, killers, killer, room_mess, killer_mess); }

You'll see this working if you clone a couple of the girls into your environment, and then set them fighting against each other. After they've exchanged a few hits, you'll see something like this:
The farmer shreds the schoolchild's chest with her fingernails. The schoolchild dies. The farmer snarls in triumph!

343

Epitaph Online

http://drakkos.co.uk

If all we want to do is have something special happen when an NPC dies, we don't need to catch the event we can just provide a second_life function. This gets executed as part of the natural processing of the death code, and can be used like so:
void second_life() { do_command (": was just faking it! } That was MURDER!")

Now she will express her poignant last words when she is unceremoniously executed by an indifferent creator:
The The The The spinster bites at the schoolchild's right foot. schoolchild dies. schoolchild was just faking it! That was MURDER! spinster snarls in triumph!

Event handling is one of the most powerful techniques you have available for providing responsive code. The important distinction is that you don't trigger events in your own code, you just provide code that should be executed when the event is triggered externally. The code should sit dormant until the trigger event occurs in the world'. There are many events that get triggered in the game. For example, we can make our zombies even more annoying by having them comment on fights in their environment:
void event_fight_in_progress(object me, object him) { if (me != this_object() && him != this_object()) { if (!random (10)) { do_command (": sniffs the air, eager for the fresh meat to come."); } } ::event_fight_in_progress (me, him); }

Each event has its own set of parameters, so you may have to consult the documentation to see exactly what information you are working with. For the event_fight_in_progress method, we have two object parameters - one for the attacker and one for the defender. Thus, we make it so that our NPC only chats if they are not involved in the fight load_a_chat handles what they should be saying when they are in combat. Events are not limited to NPCs we can catch these events in rooms and items too. Imagine for example we wanted our rooms to magically dest any corpse that appears 'Keep Zombieville Tidy' for example. If we want that to happen in a specific room, we can provide an event_enter function. This is called whenever an object enters the environment or the inventory of another object. So we'd override that function in the room where we wanted that to happen - say, for example, in our main inherit for the village: 344

Epitaph Online void event_enter(object ob, string mess, object from) { if (ob->query_corpse()) { ob->move ("/room/rubbish", "$N appear$s with a pop.", "$N disappear$s with a pop."); } ::event_enter (ob, mess, from); }

http://drakkos.co.uk

Kill a zombie in this room, and the following occurs:


> call do_death() zombie The spinster dies. The spinster was just faking it! That was MURDER! The fresh zombie corpse disappears with a pop.

Keep Zombieville tidy!

The Power of Inherits


Now, let's see why we went to the trouble of creating inherits for Zombieville. One of the things that we can now do is implement area-wide functionality just by putting an event function in an appropriate inherit for example, if we want to have corpses disappear across the entire village, we put the above event_enter function into the zombieville_room inheritable. Unfortunately when we do this and update all our inherits, we get the following warnings:
/d/support/zombieville/zombies/inherits/outside_room.c line 31: Warning: event_enter() inherited from both /d/support/zombieville/zombies/inherits/zombieville_room.c and /mudlib/inherits/room/basic_room.c (via /mudlib/inherits/outside.c); using the definition in /d/support/zombieville/zombies/inherits/zombieville_room.c. before the end of file

Yikes, what does all of that mean?? Well, remember how we talked about the idea of scope resolution? What we have here is one of the problems that multiple inheritance causes. In zombieville_room, we have put an event_enter function. However, /std/room/outside already has an event_enter, and /std/room/basic_room also already has an event_enter. These warnings are telling us that the driver can't tell which it should use, and so it has decided to just use one over the other because it doesn't know how to use both. We resolve this by providing an event_enter in inside_room and outside_room, and have that function handle the execution of the inherited functions. In inside_room:
void event_enter(object ob, string mess, object from) { room::event_enter (ob, mess, from); zombieville_room::event_enter (ob, mess, from); }

345

Epitaph Online

http://drakkos.co.uk

And then in outside room:


void event_enter(object ob, string mess, object from) { room::event_enter (ob, mess, from); zombieville_room::event_enter (ob, mess, from); }

These functions override the event_enter functionality, and tells the MUD to call the inherited event_enter first in basic_room or outside, and then in zombieville_room. This is the usual approach to resolving this kind of errors, but in certain complicated situations more complex, tailored functionality may be required. You can worry about that when it happens to you, though.

Conclusion
Our discussion about NPCs was little more than a chance for us to move the topic on to a more rich vein of inquiry the power of events in MUD coding. Events are powerful and supported at multiple levels of the MUD you can associate functionality with a particular thing happening, such as a spell being cast or someone emoting in the room, or one of the many other events that get triggered. That's a lot of power for a small, humble function. You'll have cause to explore the idea further in the code you develop from here on in.

346

Epitaph Online

http://drakkos.co.uk

Agnes McKay
Introduction
We have two additional NPCs that are part of this development the headmistresses and Agnes McKay, our survivor. Part of our creation of these NPCs will be in setting up the relationship between the two as you will undoubtedly recall from Being A Better Creator, they are former colleagues and Agnes holds the key to the mystery of what happened within the school. There are several things we'll need to put in place to make all of this work two NPCs for example! We'll also need some way of making available Agnes' notes on the player she encounters.

Agnes
Agnes is relatively straightforward herself - she's really just a normal NPC with a requirement for additional responsiveness. She's also a quest NPC, which requires us to set up the quests she has, and which she accepts as 'hand-ins'. Her basic skeleton will look like this:
#include <npc.h> inherit STD_MONSTER; void setup() { set_name ("mckay"); set_short ("Agnes McKay"); add_adjective ("agnes"); add_property ("determinate", ""); add_alias ("agnes"); set_long ("Agnes McKay is the former librarian of the Zombieville school. "She's not dead yet.\n"); basic_setup ("human", "survivor", MONSTER_LEVEL_WEAK); } "

There is nothing new here at all. Let's start off by making her hand out the first of the quests in our area - we use the add_quest method for this:
add_quest ("visiting librarian"); add_hand_in ("visiting librarian");

The add_hand_in method tells the game that when we have completed our quest, we can return to agnes and use the 'complete' command to mark ourselves as having finished. The add_quest method makes it available through the 'accept' quest, and marks it out as existing for the questsense command.
> syntax accept accept [quest] from <object>

347

Epitaph Online

http://drakkos.co.uk

> accept quest from agnes It appears that the library is still on Anges McKay's mind. It would be an act of considerable kindness if someone were to organise it and put her troubles to rest. The ones of which she is aware, anyway... You accept a quest from Agnes McKay.

We can try to complete the quest right away:


> complete visiting librarian for agnes You have not yet met the completion criteria for that quest.

It's only when our quest info contains the neccessary criteria will the quest actually complete. We can add as many quests as we like to her, so let's add the other two quests that we have:
void setup() { set_name ("mckay"); set_short ("Agnes McKay"); add_property ("determinate", ""); add_adjective ("agnes"); add_alias ("agnes"); set_long ("Agnes McKay is the former librarian of the Zombieville school. "She's not dead yet.\n"); add_quest ("visiting librarian"); add_hand_in ("visiting librarian"); add_quest ("mister gorbachev"); add_hand_in ("mister gorbachev"); add_quest ("art investigator"); add_hand_in ("art investigator"); basic_setup ("human", "survivor", MONSTER_LEVEL_WEAK); } "

Next, we need to add in an appropriate range of chats and responses to lead people to the conclusion that here be quests, and to create the narrative structure for the rest of the area. That's all stuff we learned in our previous two sections, so let's not dwell on it here.

Librarian Notes and Callbacks


When our player has completed the task of rearranging the library, we're going to have their name recorded in a journal for all to see - we'll accomplish this by allowing people to 'research' players along with the names of the people on the paintings. We already have a portrait handler set up - we'll make use of this handler to accomplish our goal. We want this to be based on when a player completes the reorganisation quest, and we can handle this in several ways - one way is to do it via an event in the 348

Epitaph Online

http://drakkos.co.uk

librarian (the event for when a quest is completed, of course), or we can do it via what is known as a callback, which is something we define in the quest data file itself. Callbacks are functions that get called when a certain thing happens - it's a way to distribute some of the coding responsibility. It also gives us a way for objects to say 'hey, you know when this thing happens? Well, drop me a line please when it does'. The function that gets called is one you decide, but the information that the function gets sent is handled by the core system. They get handled in most of the major game systems, and one of those systems is the quest system - in our quest config file, we can set an on_complete function, calling a function on our portrait handler. Now, the problem here is that our config file has no idea about our portrait handler, and we can't make it aware without including a path file that may end up being moved around, so our callback would look like this:
::->on_complete:: (: ("/d/your_name_here/zombieville/ handlers/portrait_handler")->register_completion ($1->query_name()) :)

For all the reasons why fixed paths are bad, we won't do it this way. Instead, we'll catch the event that gets generated on Agnes when a quest is completed - it's called event_quest_completed and gets two parameters - the object for the player, and the name of the quest:
void event_quest_completed (object player, string quest) { if (quest == "visiting librarian") { PORTRAIT_HANDLER->register_completion (player->query_name()); } }

Now all we need to do is add in this function to our portrait handler, and we can make it possible to research players who have completed the quest as well as the paintings that make up the quest.

Back to the Portrait Handler


Our portrait handler is pretty simple, but there's no reason it has to be shoddy. There are two kinds of methods that are used in handlers the first set of methods define the interface' of the handler they're the set of methods that developers should be using in their own code to interact with it. The rest of these are internal methods methods that exist to make the work of the handler progress smoothly. Often these will be set as private methods so that no-one can accidentally make use of them but it can be useful as a developer for the handler coder to be able to query them directly with calls and execs. That can't be done with private methods. The external interface methods should make sure they are not easily flummoxed by other coders making use of the calls for example, what happens if they pass the reference of a player rather than the name of a player? That kind of thing is 349

Epitaph Online

http://drakkos.co.uk

easy to compensate for in our code, and so we should:


void register_completion (mixed player) { string name; if (objectp (player)) { name = player->query_name(); } else { name = player; } }

It's likely that Agnes is the only object that will ever call this message, but there's no guarantee. Developer facing interfaces like this can benefit hugely by being a little more forgiving with what data they are expecting. This method will take in a mixed parameter if it's an object it gets, it sets name to be the query_name() of the object. Otherwise, it assumes a string and sets name to be whatever we got as a parameter. Now we can record some random notes about a player. First, let's have a mapping to store it:
mapping player_notes; void create() { seteuid (getuid()); all_portraits = ({ }); player_notes = ([ ]); setup_data(); }

Then, setup the notes when a player is registered:


void register_completion (mixed player) { string name; int gender; object ob; string note_text; if (objectp (player)) { name = player->query_name(); ob = player; } else { name = player; ob = find_player (name); } note_text = element_of (({ "I met $name$ today. $C$$pronoun$ is a $size$, $quirk$ person and " "smells vaguely of $smell$. $C$ muttered something about $thing$ " "and then $comic_event$.", "When $name$ $comic_event$, I realised that $pronoun$ and I were " "going to be firm friends, even discounting the terrible smell of " "$smell$ and the fact $pronoun$ seems fascinated by $thing$.", })); note_text = replace (note_text, ({ "$name$", cap_words (name), "$pronoun$", ob->query_pronoun(),

350

Epitaph Online

http://drakkos.co.uk

"$size$", element_of (({"fat", "skinny", "huge", "small", "skeletal"})), "$quirk$", element_of (({"weird", "strange", "peculiar"})), "$smell$", element_of (({"armpits", "donkeys", "cheese", "feet"})), "$thing$", element_of (({"donkeys", "monkeys", "chunkies", "flunkeys"})), "$comic_event$", element_of (({"fell down the stairs", "fell up the stairs", "fell across the stairs", "stared at some falls"})), })); player_notes[name] = note_text; } string query_player_notes (string name) { return player_notes[name]; }

The last piece of the puzzle is to then incorporate this into the research, which we can do like this:
string query_portrait_history (string head) { int i = find_portrait (head); if ( i == -1) { return player_notes[head]; } return all_portraits[i]->history; }

This allows the research command in the library to work, although it will look a little strange:
> research drakkos As far as you can tell, I met Drakkos today. He is a small, weird person and smells vaguely of armpits. muttered something about flunkeys and then fell up the stairs. You flick through some of the books in the library.

Cleaning up these messages is left as an exercise for the reader. One final note on this handler - it's no longer, strictly speaking, a portrait handler. It handles also the storing of player data. When you find that the language choice on a handler is no longer appropriate for its functions: Break it off into several handlers. Rename it appropriately, and all the methods within.

For this piece of functionality we have added, it seems overkill to create a player note handler, since we'd need to make the two handlers talk to each other anyway in order to make it work. Renaming the methods is a better strategy here, and one that is also left as an exercise for the reader.

351

Epitaph Online

http://drakkos.co.uk

Data Persistence
The last thing we need to discuss is how to make our handler save its state it shouldn't be the case that people lose their scant game immortality just because the MUD reboots. There are several ways in which data persistence is handled in the MUD. We'll talk about the simplest of these in this chapter. First of all, let's talk about how the MUD represents data in a file. Every data type has a particular kind of text representation that the driver knows how to parse into a variable reference. When an object is saved in its entirety, it is saved as a .o file, and that .o file contains the state of each variable in the object, except those marked as nosave, like so:
no_save int do_not_save_this;

When an object is saved, the state of all its variables are recorded and written to a file. For example, the .o file associated with the weather handler is as follows:
#/mudlib/handlers/weather.c pattern ({23,72,3,}) current ({6,54,5,}) variance ({10,75,20,}) mooncycle 3 moonupdate 998721449

This is the .o representation of an object, which contains the variables it is going to save, and the values those variables have. The MUD knows exactly what it is supposed to do with this string of text to turn it into an actual variable, it just needs to know when it needs to load and save things. We tell the MUD to save an object by using the save_object efun. However, this sometimes needs a little more instruction to the MUD because of the way our permissions system works.

Effective User Identifiers


Every object on the MUD has what is called an euid, which stands for Effective User Identifier'. For creators, your euid is the same as your username. For players, the euid is PLAYER. For rooms, NPCs and such, it depends on in which directory the object is located. For domain directories, the euid will be the same as the domain in which the object resides (so for /d/support/, the euid is Support. Other objects have a default euid according to the following table:

352

Epitaph Online

http://drakkos.co.uk

Directory /secure/ /mudlib/ /cmds/ /soul/ /open/ /net/ /www/secure/ /www/

Default EUID Root Room Room Room Room Network Root WWW

The euid defines what access an object has to read and write from other directories on the MUD. Objects in a /w/ directory have the same permissions as the person who owns the directory so if the creator draktest has write to /save/, so too do all objects in /w/draktest/. To see what directories each of the above euids has access to, you can use the following command:
permit summary <euid>

For example:
> permit summary WWW Euid Path WWW RW /save/www WWW RW /www

The consequence of this is that if an euid doesn't have write access to a directory, it can't write there. This ensures at least some measure of protection against rogue objects doing Bad Things. Now, this may seem like a fairly irrelevant distraction, but it relates to how saving and restoring objects works in the MUD. Let's add a function that saves the status of the handler. First, we add a new define:
#define SAVE ZOMBIEVILLE + "save/"

And then we add a save function to the disembowel_handler:


void save_me() { save_object (SAVE + "portrait_save"); }

Now, when we call this function in our own directory, it works absolutely fine. However, if someone else calls this function in our directory (someone who does not have write access to the save directory we defined) it'll give the following message:
Denied write permission in save_object().

This is because when an interactive object (such as a player or creator) is the 353

Epitaph Online

http://drakkos.co.uk

source of a call like this, the MUD notes it and says Oh-ho, you're not permitted to do this' and gives an error even though the directory in which the object resides has access. To get around this, we must say to the MUD it's okay, you can trust this guy. It's cool', which you do by using the unguarded function:
void save_me() { unguarded ((: save_object (SAVE + "portrait_save") :)); }

Unguarded says to the MUD t his is far as you need to have checked. If this object has permission, then whoever causes this function to trigger also has permission'. It's safe to do this for pretty much any code that goes into the game anyone who knows how to use this kind of thing For Evil already has access to worse ways to break our security model. For restoring the state of the handler, we use the restore_object function:
void load_me() { unguarded ((: restore_object (SAVE + "portrait_save") :)); }

All that is left at this point is to tie these two functions into the rest of the handler load the handler when the object is created, and save the object whenever its internal state changes. We need to set everything except the player data as no save, because we don't want to save the bits that get set up randomly for the quest:
nosave class portrait *all_portraits; mapping player_notes; void setup_data(); void save_me() { unguarded ((: save_object (SAVE + "portrait_save") :)); } void load_me() { unguarded ((: restore_object (SAVE + "portrait_save") :)); } void create() { seteuid (getuid()); load_me(); if (!all_portraits) { all_portraits = ({ }); } if (!player_notes) { player_notes = ([ ]); } setup_data(); }

We also need to make sure that we save every time a name gets added: 354

Epitaph Online

http://drakkos.co.uk

void register_completion (mixed player) { // Blah blah player_notes[name] = note_text; save_me(); }

One last thing to discuss about saving objects we can also tell the MUD to compress save files. This is usually a good thing to do, especially when working with large amounts of data we just pass an additional parameter to save_object, like so:
void save_me() { unguarded ((: save_object (SAVE + "portrait_save", 2) :)); }

Now when the handler saves it saves as a gzipped file for big files, this will save huge amounts of disk space and file I/O at a cost of a little CPU. It's a bargain whatever way you look at it!

Conclusion
Our NPC in this chapter is not an especially complicated one, but it led us into a discussion of the way file permissions work on the MUD, and what we can do to restore the state of objects when we need it. Data persistence is not tricky to do there's not much more to it than we have talked about here. For very complex, or very intensive file I/O, we may need to do something a little more tailored. For most objects and handlers, this is as much as you ever need to know.

355

Epitaph Online

http://drakkos.co.uk

Function Pointers
Introduction
Before we talk about the headmistress, we're going to touch on the topic of function pointers. We've been using these for a while now, and the final step that we are going to take is to explain what they are, how they work, and what they can do for your code. These get used an awful lot on Epitaph, and it's high time we took some time out from our exciting coding to talk about them properly.

The Structure of a Function Pointer


All the way through our code we've been using variables, and these are just containers for some information that we want to store. A function pointer is a variable too, a variable of type function. However, unlike the other data types which are passive, a function pointer contains some actual code that can be executed (or evaluated) on command. Function pointers of the type we've been working with are what I like to refer to as happy code' because they're enclosed in smiles like so:
function f = (: 1 + 2 :);

One way to think of a function pointer is as a delayed blast function'. It sits there until someone decides it's time for the code to be triggered through the use of the evaluate efun:
return evaluate (f);

Now, this example is pretty trivial the real power of function pointers comes from how flexible they are. We can make use of placeholder variables inside a function pointer like so:
function f = (: $1 + $2 :);

Then when we evaluate the function, we can pass parameters to it. They get handled in the pointer in the order in which they are provided:
return evaluate (f, 10, 20);

When the function gets evaluated, 10 gets substituted for $1, and 20 for $2. We can bind the function pointer to a local function this is something we've done quite a lot of with add_items. Think back to when we made the add_item you could vault, way back in Introductory LPC:
add_item ("jagged rock", ({ "long", "This is a jagged rock.", "position", "on the jagged rock",

356

Epitaph Online "vault", (: do_vault :), ({"kick", "punch"}), "Ow! }));

http://drakkos.co.uk

That stung!\n",

What we've done here is create a function pointer that binds to the locally defined function called do_vault. When this function pointer is evaluated, it calls the defined function. We can even do this with arguments, if we so desire:
int test_function(int a, int b) { return a + b; } int test_pointer() { function f = (: test_function, 10, 20 :); return evaluate (f); }

All of this works perfectly. We can also define function pointers that look, to all intents and purposes, like normal function calls:
function f = (: test_function ($1, $2) :);

These kind of function pointers cause problems with debugging though when an error occurs in one of these, the runtime trace tells us simply that there is a problem with the pointer, but very little useful information beyond that. The last kind of function pointer lets us simply write a function and store it in a variable. This is horrible in all sorts of ways, so please don't do it. However, you may find other people doing it in other parts of the mudlib, so you should at least be able to recognize it when it occurs:
int test_pointer() { function f = function (int a, int b) { return a + b; }; return evaluate (f, 10, 20); }

If I see you doing this anywhere, I will cut you. You have been warned! All of these are different ways of achieving the same end create a delayed blast function' that gets evaluated at a later date. The power of this as a mechanism can't really be over-stated it lets you bundle an awful lot of functionality into a very small space if you know what you're doing. To see the power invested in this kind of data type, we're going to talk about what I like to refer to as the Four Holy Efuns.

357

Epitaph Online

http://drakkos.co.uk

The Four Holy Efuns


There are four efuns that, when used in combination with function pointers, will make available a huge amount of functionality with very little expended effort. They are implode(), explode(), filter(), and map(). The first two we have seen. The last two we will talk about in depth in this chapter. Filter is an efun that takes an array, and then returns another array that contains only those elements that passed a particular check. This check can be defined as a local function, or implemented as a function pointer. Within the function pointer, the argument $1 refers to the current element' in the same way our variable in a foreach loop refers to the current element of the array we are looping though. Let's say for example that we wanted to get the list of online creators. One line of code can do that:
return filter (users(), (: $1->query-creator() :));

The array we provide to the efun is the object array returned from the users() function. We step over each element of that array, calling query_creator on each. Those objects that return 1 from that are added to the array to be returned. Those that return 0 are discarded. Do you want to get the list of interactive objects in the room in which you are currently standing? Try this in an exec:
exec return filter (all_inventory (environment (this_player())), (: interactive ($1) :));

The process is exactly the same filter steps over each element of the array and passes it as an argument in the interactive efun. Those objects that return 1 get returned from the filter. The sister function of filter is map, and it works in the same way the difference is, it doesn't filter out the objects, it gives the return value of whatever function we provided it. Would you like to get the names of all players online? Well, you can do this:
return map (users(), (: $1->query_name() :));

The process that map goes through is to get the array provided by users, call query_name() on each element, and take the result of that function call and add it to an array to be returned. What we get out of that then is an array containing all the names of each online player. Now, here's where we start getting a bit adventurous. The real power of these efuns comes from when we combine them together. What if you want the names of all online creators? Really that's the process we went through above, except the result of one feeds into the other: 358

Epitaph Online string *online_creator_names() { object *creators = filter (users(), (: $1->query_creator() :)); return map (creators, (: $1->query_name() :)); }

http://drakkos.co.uk

You will often find these chained together, especially if you need to quickly check something with an exec:
exec return map (filter (users(), (: $1->query_creator() :)), (: $1->query_name() :))

Rapidly, code like this becomes very difficult for people to effectively read. Function pointers give a huge amount of expressive power with a minimum of coding effort, but the cost is in casual readability of your code. Nonetheless, the benefits are hard to deny. Implode can also take a function pointer as its second parameter. When we do this, it takes the first and the second parameters of the array we are imploding, and passes them into the function. It then takes the result of that and the next element, and passes them in, and so on. So imagine if we had an array of integers, like so: Index 0 1 2 3 Element 10 20 30 40

And now imagine that we ran the following implode over it:
implode (arr, (: $1 + $2 :));

For the first step of the implode, the function gets elements 0 and 1:
10 + 20

The result of this is 30, and at the next step of the implode, the function gets the results of the previous evaluation (30), as well as the next element to be imploded (element 2):
30 + 30

And then at the last, it gets the results of the evaluation plus the last remaining element:
60 + 40

The result of this implode is to return the value 100 it sums up all of the elements in the function pointer in a quick, elegant way. Explode doesn't permit the use of a function pointer, but it's the natural companion to implode as thus still remains one of the Four Holy Efuns. 359

Epitaph Online

http://drakkos.co.uk

Taking the four of these together gives tremendous expressive power to a creator when dealing with strings or arrays. For example, let's say I have the following string:
"drakkos,ploosk,haug,eek,hambone"

And what I want is to check to see if these people are online, and if they are display their properly capitalized names. I can do that with a function:
string fix_names (string str) { string *names = explode (str, ","); string *online = ({ }); string ret; for (int i = 0; i < sizeof (names);i++) { if (find_player (names[i])) { online += ({ cap_words (names[i]) }); } } ret = implode (online, ", "); return ret; }

Or I can do it in a much more compact way with the four holy efuns:
return implode (map (filter (explode (str, ","), (: find_player ($1) :)), (: cap_words ($1) :)), ", ");

You will find much of our code is based around this kind of compact representation. However, this should all come with a disclaimer there is very little that you can do with function pointers that you cannot do with explicit functions, and these are always more readable and maintainable. Working effectively with function pointers is the key to understanding lots of the mudlib, and to making your exec command the best friend you ever had, but there are only a few situations in which you should really use them. Many of us over the years have gotten into the bad habit of using them largely automatically, and you would be doing yourself a favour if you used them only sparingly. However, we are now in a position to look at the areas where we have used function pointers in our code, and why we have gone down that route. Throughout Zombieville (and indeed, Deadville), function pointers were used only when they were the only way to achieve the necessary ends.

Back to the Library


Look at our library it's beautiful. It is absolutely bursting with quests, and we should be proud of ourselves that we have them. However, one last thing remains the long description of the library:
set_long ("This is the main part of the library. "discarded books thrown everywhere!\n"); It's a mess, with "

360

Epitaph Online

http://drakkos.co.uk

Here, we need to have something a little more dynamic it doesn't make sense to have this long description when the books have all been sorted away. Instead, the long description should change with the state of the library. Now, if we have a long description that can change in the course of play, we can make a dynamic long description by making use of a function pointer. There are other ways to do it, but they are awkward. All we do is something like this:
set_long ((: library_long :));

And then a function to return a string based on the state of the library:
string library_long() { string ret = "This is the main part of the library. ";

if (check_complete()) { ret += "It is immaculate, with books neatly stored on shelves. "; } else if (!sizeof (query_unsorted_books())) { ret += "It is very tidy, but it doesn't look as if the books have " "been sorted properly."; } else { ret += "It's a mess, with discarded books thrown everywhere!"; } return ret + "\n"; }

Now, this is a good way of providing a dynamic long but there are other options. If the state of the library is going to change a lot, then it's fine. If it's going to change once, then consider simply setting the long to be something different at a later part of the code. Internally, the set_long that we give the room gets stored as the variable long_d in /mudlib/mixins/basic/desc. When the long description is queried (for example, when we do a look in a room), the following function gets triggered:
varargs mixed query_long(string str, int dark) { if (functionp(long_d)) return evaluate(long_d); return long_d; }

This is the difference between setting a function pointer as the long, and setting the return value of a function. For example, consider the following:
set_long (library_long());

This is evaluated only when the set_long is called whatever comes out of that function is what becomes the string stored in long_d. As such, this just creates a static description the only difference is where that static description comes from. If we store it as a function pointer it means that every time the query_long 361

Epitaph Online

http://drakkos.co.uk

function is called, the function pointer gets evaluated. As I'm sure you can imagine, that's not great for the efficiency of your code if it's not necessary, especially if your function requires a lot of CPU time to process. We can take that hit every now and again if there is suitable benefit, but it's not something to get into the habit of doing. It is exactly this same principle at work when we set a function pointer to be evaluated in an add_item. That function gets evaluated every time it is accessed, so if it's used in place of the descriptive text, it'll be called whenever you look at the item. If it's used as the response to a command, then it'll be called whenever that command is triggered.

Function Pointer Support


Function pointers are not supported universally throughout the mudlib they require someone to have put the support in place in the low level functions. However, they are supported widely. For example, if you wish to change the name of a function attached to an add_command, you can do that with a function pointer. Let's say we wanted to give players the o ption to either read' our books or browse' them, with the same thing happening in either case. We simply use a function pointer as a third parameter to add_command:
add_command ("read", "blurb on <string'book'>"); add_command ("browse", "blurb on <string'book'>", (: do_read :));

If we use a function pointer in this way, we always need to prototype the function because the driver will do compile time checking on the code to make sure the function is present. We can even use function pointers to simplify the parameter list of the methods themselves. For example, we don't care about anything other than the blurb the player entered for the do_read function, and yet we still have a pile of parameters we need to represent:
int do_read (object *indirect_obs, string dir_match, string indir_match, mixed *args, string pattern) { }

We can use the function pointer to say actually, all I want is the first element of the fourth parameter':
add_command ("read", "blurb on <string'book'>", (: do_read ($4[0]) :)); add_command ("browse", "blurb on <string'book'>", (: do_read ($4[0]) :));

And now, rather than our complicated list of parameters, we just have one string coming in:
int do_read (string book) { }

We do that a lot around these here parts. As long as you're clear in your mind as 362

Epitaph Online

http://drakkos.co.uk

to what the little dollar sign numbers mean, it should be pretty simple for you to work out what information is going into the functions. Often, we use a different kind of way of supporting functions that are dynamically called from other functions. Where possible, you should use these rather than pointers. A case in point is in a load_chat or add_respond_to_with, where we use the # notation. These are handled in a different way in the mudlib. They are not function pointers, they get parsed by lower-level mudlib inherits to do the right thing. Really all that happens is when this chat or response is selected, the function checks to see if the first character of that response is a #, and if it is it uses the function call_other to trigger the function response. This is taken directly from expand_mon_string in /mudlib/inherits/monster:
switch ( str[ 0 ] ) { case '#' : args = explode(str[ 1..], ":"); call_other( TO, args[0], args[1..]... ); break;

The # system is supported erratically throughout our mudlib, but you should use it instead of a function pointer whenever you can. Function pointers are tricky for others to read unless they are thoroughly steeped in sin, difficult to debug in certain cases, and do not benefit from any specialized functionality that comes from more tailored support in the mudlib.

A Few More Things about Function Pointers


You can pass parameters to a function pointer, but when doing so you can't make use of local variables:
string *online_creator_names(string is_online) { object *creators = filter (users(), (: $1->query_name() == is_online :)); return map (creators, (: $1->query_name() :)); }

This will not work you'll get a compile time error saying You can't use a local variable in a function pointer'. If you want to use a variable like this, you need to explicitly force them to be literals rather than variables by enclosing them in $(), like so:
object *creators = filter (users(), (: $1->query_name() == $(is_online) :));

This syntax tells the MUD 'hey, I'm just going to need the value of this - extract it from the variable, because the variable might not be around later'. You will also find that sometimes function pointers cause all sorts of strange errors. Function pointers are bound to a particular context (a specific object), and 363

Epitaph Online

http://drakkos.co.uk

they don't save with save_object or work very well in call_outs. The error you'll see in this case is along the lines of Owner of function pointer is destructed', and it will cause a runtime.

Conclusion
Powerful as all get out, but costly in terms of code readability and often efficiency function pointers are the guilty little indulgences of Epitaph. We have comprehensive (if not universal) support for them in our mudlib, and they are the key to making your exec command a powerful weapon rather than a distracting novelty. It is important that you know how to use them, because there are very few parts of our game that aren't touched by them in some way, and many of the things you might like to do require at least passing familiarity with the concept. Think way back to Introductory LPC we couldn't even keep them out of the introductory text for Epitaph. Many of us use them automatically, but that's not a good thing. They should be used with caution and with forethought. Whenever it is possible, use good, honest, properly written functions rather than function pointers. It may cost you a little extra time, but those who have to maintain your code will thank you for it.

364

Epitaph Online

http://drakkos.co.uk

The Headmistress
Introduction
Now that we've happily implemented our zombies and our survivor, let's write up the last of our NPCs. The headmistress is a pretty straightforward character - a boss zombie with all sorts of neat powers designed to make her a challenge. She is both a boss, and a zombie, so we use the base inherit STD_BOSS_ZOMBIE for her. Remember what our intentions are here - a few boss powers, some semirandomised articulations, and a whole lotta heart! So let's get started!

The Skeleton of a Boss Zombie


Our skeleton file for the headmistress looks like this:
#include <npc.h> inherit STD_BOSS_ZOMBIE; void setup() { set_name ("headmistress"); add_property ("determinate", "the "); set_long ("The infection has not been kind to this poor woman - her body " "has erupted in violent, pus-filled boils and blisters that eject " "greasy fluid like minature volcanos. Her fingers are like iron claws, " "hooked and sharpened to impossible points at the nails.\n"); basic_setup ("zombie", "standard", MONSTER_LEVEL_BOSS); set_boss_level (1); }

The three main things here are the use of the STD_BOSS_ZOMBIE inherit, the basic_setup which sets her as a MONSTER_LEVEL_BOSS, and the set_boss_level call - this sets up the level of her skills - when we set up the skills of rank and file NPCs, there is some randomness (within certain ranges). It is important though that for bosses there is reliability - if people fail to kill a boss, it should be because their strategy was ineffective or the execution was flawed, not because they were unlucky enough to get a boss that had an extra bonus of 100 on all the key skills. When we talked about the headmistress in the previous section of the book, we discussed having her semi-articulate - being able to say things, but not being able to fully express them because, well, she's dead. So every so often she will try to speak, but other times she will simply moan, or groan, or growl. We want her growling to be vaguely animalistic, and animals don't growl the same 365

Epitaph Online

http://drakkos.co.uk

way each time so let's write a function to do some semi-randomised growling, like so:
void zombie_growling() { string *start_letters = ({"Gra", "Ra", "Kra", "Bwa"}); string *start_bit = allocate (3 + random (4), "a"); string *mid_bit = allocate (3 + random (4), "r"); string *end_bit = allocate (3 + random (4), "a"); do_command ("say " + element_of (start_letters) + implode (start_bit, "") + implode (mid_bit, "") + implode (end_bit, "") + "r!"); }

In order to speak, an NPC needs a language, and the easiest way to get that is via a nationality - we can set that up like we did with Beefy way back when:
setup_nationality (MUDLIB_NATIONALITIES + "british", "scotland");

Implode is a function that takes an array and condenses it down to a string, and allocate lets us create an array the first parameter is how many elements the array will have, and the second is the starting value to give each element of the array. The result is that the Headmistresses growls vaguely realistically:
The The The The headmistress headmistress headmistress headmistress exclaims: exclaims: exclaims: exclaims: Bwaaaaaarrrrraaaaaar! Graaaaaarrraaaar! Kraaaaaaarrraaaaar! Kraaaarrraaar!

There's no real need for that of course, it's just a l'il bit of sugar for our star attraction. Next, we want our 'speech but not speech'. We can handle this by giving each response an unmangled form, and pass it through a mangling function. Our mangling function will change random parts of the string into grunts, groans and moans. We should also make sure that we reuse code where we can:
string random_growling_bits() { string *start_bit = allocate (3 + random (4), "a"); string *mid_bit = allocate (3 + random (4), "r"); string *end_bit = allocate (3 + random (4), "a"); return implode (start_bit, "") + implode (mid_bit, "") + implode (end_bit, ""); } void animal_growling() { string *start_letters = ({"Gra", "Ra", "Kra", "Bwa"}); do_command ("say " + element_of (start_letters) + random_growling_bits() + "r!"); } string contort_word (string word) { string start = word[0]; string end = word[<1]; return start + random_growling_bits() + end;

366

Epitaph Online } string mangle_string (string str) { string *arr = explode (str, " "); for (int i = 0; i < sizeof (arr); i++) { if (!random (3)) { if (sizeof (arr[i])) { arr[i] = contort_word (arr[i]); } } } return implode (arr, " "); }

http://drakkos.co.uk

It's the mangle_string function here that handles changing a chat into a contorted string at random intervals throughout the string, it'll use the contort_word method to turn a word like Hello' int o the word 'haaarraarrro' or such. Try to update this NPC though, and we get the following:
Type mismatch ( string vs int ) when initializing start before Type mismatch ( string vs int ) when initializing end before

This is all down to how the MUD actually represents individual characters of a string it stores them as numeric representations of the letter they are supposed to be. Technically they get stored as ASCII codes, each number representing a particular letter. So when we get a single character off of an string (such as word[0]), what we get out is the number representing that letter. As such we need to store them in ints:
string contort_word (string word) { int start = word[0]; int end = word[<1]; return start + random_growling_bits() + end; }

Now it compiles, but we have a new problem. Try to call the function with a testing string, and we get something rather strange back:
> call mangle_string ("get in ma belly") headmistress Returned: "get 105aaaaarrrrrraaaaa110 ma 98aaaarrrrrraaaaaa121"

Say what? What? Why is this! Why do you hit us, LPC, when we show you nothing but love? Well, I'm sure you can guess we represented letters coming off of our string as integers, and so that's how they get added to the string - as numbers. If we want to turn these numbers into characters, we need to pass them through sprintf and tell it treat this number I give you as a character' we do that with the %c code:
string contort_word (string word) { int start = word[0]; int end = word[<1]; return sprintf ("%c", start) + random_growling_bits()

367

Epitaph Online + sprintf ("%c", end); }

http://drakkos.co.uk

To see what's actually happening here, try this little exec:


exec int num = 97; for (int i =0; i < 26; i++) { printf ("%c\n", num + i); }

This will print out all the letters of the alphabet. To see it print out the numbers instead, use the %d code for sprintf:
exec int num = 97; for (int i =0; i < 26; i++) { printf ("%d\n", num + i); }

This second exec does the same thing as the first, except what you'll get out of it are the numeric ASCII codes. Some C-type languages have a char' datatype to simplify the process. Unfortunately LPC doesn't, and so we need to make use of these kinds of workarounds. Anyway, we want to bind both the growing and talking into load_chats for our little zombie boss. One final methid:
void do_zombie_chat (string str) { str = mangle_string (str); do_command ("say " + str); }

And then set that up in our load_chats:


load_a_chat (10, ({ 1, "#zombie_growling", 1, "#do_zombie_chat:Get in ma belly!", 1, "#do_zombie_chat:They were delicious!", 1, "#do_zombie_chat:The boys tasted like chicken!", 1, "#do_zombie_chat:The girls tasted like sugar!", }));

We can have as many of these chat strings as we like, and if we want the horrible grunts and growls to be even more effective, we just need to change the mangle_string function to make it so.

NPC Combat Behaviours


You'll have noticed that many NPCs across the game make use of fairly solid (albeit unimaginative) tactics as they fight with players. They launch specials, trip people up, use the right kinds of weapons and use the right kind of defences. Every NPC on Epitaph gets access to a behavioural subsystem that lets you define new and interesting behaviours for them, or choose from the a la carte menu of basic functionality. All of these behaviour templates are located in /data/behaviours/ - the one for a basic zombie looks like this: 368

Epitaph Online

http://drakkos.co.uk

::item "basic zombie":: ::->minimum_time_between_specials:: 10 ::->energy_in_reserve:: 500 ::->preferred_position:: POSITION_STATE_SCRUM ::->breach_chance:: 100 ::->combat_actions:: ([ (: $1->query_prone() == 0 :) : "wrestle $target$", (: $1->query_prone() && sizeof ($1->query_pinned()) < 2 :) : "pin $target$", ]) ::->on_consider:: ([ ]) ::->barricades_modules:: ({ "zombie barricades" }) ::->applied_state:: ([ ])

If you think 'that looks like a quest file', then you're right - it's another data file which is interpreted by another handler. There are only a few bits here that are really revelent for now - preferred_position handled the default position the NPC will attempt to reach, and the minimum time between specials is the time it will wait, at a minimum, before attempting to perform a 'clever' combat action. It is the combat_actions part that defines those 'clever combat actions' - and now you see why we had to take time out to talk about function pointers before we got here. Combat actions are defined as mappings - the key contains some 'prerequisite checks' that must be passed before the action (the value of the mapping) is made available. That action can either be a string that the NPC will perform (with $target$ being replaced with the object reference of the current target on the NPC), or a function pointer that contains more exotic functionality. Let's look at our two combat actions here to see what is going in. For the first, we check to see if our target (denoted by $1) is prone. If they are not prone, one of the options we have for a 'clever' action is to wrestle them to the ground. For the second, it checks to see if they are prone, and if they are currently being pinned by fewer than two opponents. If both of these things are true, we also have an option to attempt to pin an enemy to the ground. When it comes time to perform a special action, the NPC will evaluate all its prerequisites, and construct an array of possible actions - one of these will be picked at random and performed. This ensures that NPCs only perform those actions that make sense in their current context. Now, the problem here with our boss NPC is that it's not a normal zombie - it's a boss zombie, and it shouldn't simply try to wrestle people to the ground. There is a behaviour that simply makes our boss auto-attack like most unintelligent NPCs, and it is called basic zombie boss. We make our headmistress use this by using the function set_fixed_behaviour in her setup:
set_fixed_behaviour ("basic boss zombie");

369

Epitaph Online

http://drakkos.co.uk

This will make sure her boss powers are the only thing she really has to work with.

Boss Powers
You will, I am sure, be entirely unsurprised to find out that boss powers get handled via data files - they look like this:
::item "driver aoe knockdown":: ::->start_text:: ":roars and starts to push hard against the sides of the " "carriage." ::->end_text:: ": stops pushng the side of the carriage." ::->valid:: (: 1 :) ::->aoe:: 1 ::->delay:: 5 ::->defence_task:: TASKMAP_ASSESS_AN_ENEMY_INTENTION ::->difficulty:: TTSM_DIFF_EXTREMELY_EASY ::->messages:: ({"You learn a little something about keeping your feet."}) ::->min_damage:: 50 ::->random_damage:: 50 ::->damage_type:: "blunt" ::->target_text:: "The shaking carriage knocks you onto your back!\n" ::->room_text:: "The carriage shakes alarmingly!\n" ::->extra_payload:: ({ (: $1->set_prone ($2->modify_value_by_power (10 + random (5), $3)) :) }) ::->cooldown:: 10 ::->freeze:: 1

This is a boss power from an existing NPC - the train driver in the dangling train carriage in Dunglen. Most of it should be fairly self-explanatory, so Im not going to spend an awful lot of time explaining it - just those bits that are relevent to us. Bosses are constantly ready to use their powers, the only thing that stops them is the cooldown - they will only ever perform one power at a time, but the one they perform is just based on their powers and cooldowns. Let's look at one example of a power file for the disarming attack our NPC has:
::item "headmistress claw":: ::->start_text:: ":screeches and flexes her claw like fingers." ::->end_text:: ": stabs a clawed hand towards her target." ::->no_apply:: ": wails angrily as her target evades her attack." ::->valid:: (: 1 :) ::->aoe:: 0 ::->delay:: 5 ::->defence_task:: TASKMAP_ASSESS_AN_ENEMY_INTENTION ::->difficulty:: TTSM_DIFF_EXTREMELY_EASY ::->messages:: ({"You learn a little more about keeping your weapon " "in your hand."}) ::->min_damage:: 50 ::->random_damage:: 50 ::->damage_type:: "blunt" ::->target_text:: "The headmistress snatches your weapon out of your hand!\n" ::->room_text:: "The headmistress snatches a weapon right out of $target$'s hand!\n"

370

Epitaph Online

http://drakkos.co.uk

::->extra_payload:: ({ (: $1->fumble_current_weapon ($2->modify_value_by_power (10 + random (5))) :) }) ::->cooldown:: 30 ::->freeze:: 1 ::->apply_test:: (: sizeof ($1->query_weapons()) :)

We need to add this boss power to our headmistress's setup:


add_boss_action ("headmistress claw");

Facing off against our headmistress, she will now perform her steely hand attack.
The headmistress screeches and flexes her claw like fingers. You slash at the headmistress with your machete but it just dodges out of the way. The headmistress snatches your weapon out of your hand!

Her other attacks are handled in exactly the same way - let's look at one more, and leave the third as an exercise for the reader. We're going to look at her pus explosion attack, because that offers us a few more opportunities to look at how boss powers function.

Pus Attack
As a recap - this attack is going to explode over everyone who is in melee range, and it is going to make her do dramatically more damage to anyone who is coated in her pus. The basic structure of the attack is the same as the the one above - a data file which we add using add_boss_action:
::item "pus attack":: ::->start_text:: ": starts to shudder, her pus-filled boils starting to weep and pulsate." ::->end_text:: ": screams, and a volano of hot, sticky pus exploded out like a shell of vileness." ::->valid:: (: 1 :) ::->aoe:: 1 ::->delay:: 5 ::->target_text:: "Urgh, you are covered in disgusting pus!!\n" ::->room_text:: "The headmistress explodes in a spray of disgusting pus!\n" ::->extra_payload:: ({ (: $1->add_stain ("pus", 1) :) }) ::->cooldown:: 60 ::->freeze:: 1 ::->apply_test:: (: $1->query_combat_state() == POSITION_STATE_SCRUM :)

And then:
add_boss_action ("pus attack");

And now, when we fight our boss, we get:

371

Epitaph Online

http://drakkos.co.uk

The headmistress starts to shudder, her pus-filled boils starting to weep and pulsate. The headmistress screams, and a volano of hot, sticky pus exploded out like a shell of vileness. Urgh, you are covered in disgusting pus!!

Urgh. It doesn't do anything at the moment - it just adds a stain through our stains system. There were all sorts of other ways we could handle this - an antiknack, an effect, or a property - it doesn't really matter. All that matters is there is some way for our NPC to tell how much pus someone has been covered with:
He is spattered with pus, caked with blood and caked with paint.

The last bit of this attack is to make her do extra damage based on how much pus someone has been covered with. We do that by over-riding modify_damage in our headmistress:
varargs int modify_damage( int damage, string attack_name, object player ) { damage = ::modify_damage (damage, attack_name, player); if (player->query_stain_amount ("pus")) { damage *= player->query_stain_amount ("pus") + 1; } return damage; }

And guess what! That is our second boss power implemented. The last of these I shall leave as an exercise for the reader.

Boss Drops
As a reward for downing a boss, we like to offer some potentially unique items as a 'drop' for those who participated. Not everyone gets a chance at the look, that's handled by our loot chest system. Making a drop available is simple - let's make one available from our headmistress - that's done using add_drop:
add_drop ("bag of dog food", 50);

The first is the item we want to add as a potential drop, and the second is its chance (this gets done via a roulette wheel as we've discussed previously). Now when we kill our headmistress, we get a nice reward:
You see a loot chest out of the corner of your eye.

Uh, thanks miss. I'll cherish this dog food forever.

372

Epitaph Online

http://drakkos.co.uk

Conclusion
Boss NPCs are well known in many online games, and Epitaph is no different - our boss NPC system offers a convenient way to make genuinely interesting combat encounters for groups, and also provides a way to make available unique items that are not available anywhere else. Of course, no encounter will be interesting if time isn't taken to design it properly, so always make sure that you have thought through the different parts of the encounter - design here is much more important than implementation. We still haven't talked about how to make the basement of the school an instance - don't worry, that will come.

373

Epitaph Online

http://drakkos.co.uk

Shopping Around
Introduction
There's a feature we haven't yet addressed in Zombieville that of our tartan knitwear store. It's not a major deficiency, but it's still something we have left to do. Along the way, we'll learn about a system called autoloading, which is the system that is used to make items save their properties over logins.

Item Development
We spoke about clothes and virtual files in the first Introductory LPC, so we won't rehash that here. First of all, let's write a basic skirt for the shop. We won't write it as a virtual file (for reasons that will become clear later), we'll do it as a normal LPC .c file. First of all though, we need to talk about where we're going to save these files. The logical place to save them, considering the way we have been saving things so far, is to save them in an items' sub-directory of Zombieville. Unfortunately, this doesn't work as well as we might hope the armoury will not pick items out of sub-directories in this way. The only way the armoury knows where to find objects is if they are stored in /d/some_domain/items or a sub-directory of same. This is a huge drawback for the way we've been doing things it means we suddenly need to switch from storing things in one easily explored directory and navigate instead across two different directories. We're not going to do that we are going to be awkward and store things in zombieville/items. This is purely something we are doing to make these tutorials hang together properly when developing code in the real world', it should always be accessible from the armoury. Let this be a warning! Anyway, we're doing the following:
#define ITEMS ZOMBIEVILLE + "items/"

And then we're going to create a simple dress item in that directory:
inherit STD_CLOTHING; void setup() { set_name ("skirt"); set_short ("$colour$ $material$ skirt"); add_adjective (({"tartan"})); set_main_plural ("$colour$ $material$ skirts"); set_long ("This is a $colour$ skirt, like you could imagine being worn " "by a Scottish grandmother against the unforgiving Caledonian " "winters. It's made of $material$.\n"); set_material (([ "wool" : 1])); set_colour ("tartan");

374

Epitaph Online set_type ("skirt"); }

http://drakkos.co.uk

So far, so straightforward. Now, let's get a little more adventurous.

Auto Loading
When a player saves, we go through a process whereby we take their skills, tell history, health, wellbeing values, and all the other things that make up that unique player and we store that info in a file on disk. As part of that process, we also store their inventory, including all those elements of each item that are subject to change as the game goes on, such as stains, condition, engravings, and so forth. This is a fairly complicated procedure, and is known as the auto load system. The consequence of this is that creating a player item that saves its state doesn't work like creating a handler that saves its state we need to hook into the auto load process. Each object that inherits STD_OBJECT has a method called query_dynamic_auto_load' defined in it. It is this function that contains all of the information that makes one object unique and distinct from another object. For example, if I call it on a machete in my inventory, I get the following output:
Returned: ([ /* sizeof() == 4 */ "upgrade" : ([ /* sizeof() == 1 */ "upgrade : upgrades" : ([ ]), ]), "::" : ([ /* sizeof() == 10 */ "properties" : ([ /* sizeof() == 4 */ "virtual clone" : "/mudlib/inherits/weapon", "salvageable" : 1, "shop type" : "armoury", "help file name" : ({ /* sizeof() == 1 */ "weapon" }), ]), "gore" : 0, "keep" : 0, "materials" : ([ /* sizeof() == 1 */ "steel" : 1, ]), "cloned by" : "drakkos", "read mess" : ({ }), "gore message" : "", "identify" : 0, "colour" : "silver", "light" : 0, ]), "hold" : ([ /* sizeof() == 2 */ "limbs used" : 0, "limb" : -1, ]), "condition" : ([ /* sizeof() == 2 */ "cond" : 7350, "lowest cond" : 6762, ]), ])

375

Epitaph Online

http://drakkos.co.uk

In there is all the information the MUD needs to take a basic machete and turn it into the specific machette I know I love. This mapping gets built through successive specializations of the method in each object that inherits another. Essentially each file takes the responsibility for adding its own state to this mapping, and then passing the call along the line to the next inherit in the chain. Let's look at query_dynamic_auto_load as defined in the scabbard:
mapping query_dynamic_auto_load() { mapping map; map = ([ "::" : object::query_dynamic_auto_load(), "condition" : condition::query_dynamic_auto_load(), "hold" : holdable::query_dynamic_auto_load(), "upgrade" : upgrade::query_dynamic_auto_load(), ]); return map; }

Notice that as part of this method, it makes a call to the query_dynamic_auto_load of its parent objects, object, condition, coldable, and upgrade:
"::" : object::query_dynamic_auto_load(), "condition" : condition::query_dynamic_auto_load(), "hold" : holdable::query_dynamic_auto_load(), "upgrade" : upgrade::query_dynamic_auto_load(),

The first line here takes whatever comes out of query_dynamic_auto_load for the object, and stores it in the mapping key "::", and whatever comes out of the condition call gets stored in the "condition" key, and so on. These values for these keys thus contain the mappings that come out of the parents, and the parents themselves contain mappings that come out of their parents. Let's take a short break from this, and go back to our skirt.

Dressing Up
Let's say I want these skirts to be a little more configurable than normal dresses. In addition to having the short and the long, I want them to have a specific 'clan tartan' that can be set to define the pattern. I could create dozens of different clothes files to do this, but that's a maintenance and development nightmare. The easiest solution is to let me set it directly on the skirt:
inherit STD_CLOTHING; string motif; void setup() { set_name ("skirt"); set_short ("$colour$ $material$ skirt"); add_adjective (({"tartan"}));

376

Epitaph Online

http://drakkos.co.uk

set_main_plural ("$colour$ $material$ skirts"); set_long ("This is a $colour$ skirt, like you could imagine being worn " "by a Scottish grandmother against the unforgiving Caledonian " "winters. It's made of $material$.\n"); set_material (([ "wool" : 1])); set_colour ("tartan"); set_type ("skirt"); add_extra_look (this_object()); } void set_motif (string m) { motif = m; } string query_motif() { return motif; } string extra_look (object ob) { if (query_motif()) { return "The skirt has been picked out in the tartan of the " + query_motif() + " clan.\n"; } return ""; }

Now when I clone one and call set_motif, I get the following:
This is a tartan skirt, like you could imagine being worn by a Scottish grandmother against the unforgiving Caledonian winters. It's made of wool. The skirt has been picked out in the tartan of the MacGregor clan. It is in excellent condition.

Lovely, just what I have always wanted. Alas, it is ephemeral, like the love of a young girl. If we log off and then back on, then the motif disappears:
This is a tartan skirt, like you could imagine being worn by a Scottish grandmother against the unforgiving Caledonian winters. It's made of wool. It is in excellent condition.

We know we wanted the motif to be saved. The MUD alas does not. This is where we need to hook into the auto load code.

Back To The Auto Loading


See, this auto load stuff is not a magical mystery tour this kind of state storing is a very common thing creators want to do. It's not actually hard for us to hook into this process, but it's useful to understand why we're doing it rather than assuming it to be some kind of magical transaction. We need to provide our own specialization of query_dynamic_auto_load to store the motif along with everything else:
mapping query_dynamic_auto_load() { return ([ "::" : ::query_dynamic_auto_load(),

377

Epitaph Online "motif" : query_motif(), ]); }

http://drakkos.co.uk

In the mapping we create, the key ::" holds the result of the call on our parent class (/mudlib/inherits/clothing) which does the same kind of thing for its parents, all the way along the line. Additionally, we add a motif entry to the mapping - this holds what our motif was set as. We don't need to call this method at any point, it gets called naturally as part of the saving and logon process. Now we can log off and log on, and... it's still not there. Damnation! Why is this the case? Well, we've saved the information fine. The second part of the auto load process is that each object is also responsible for restoring its own state. This works identically as a process, but we use the method init_dynamic_arg to do this. As with query_dynamic_auto_load, this method gets called automatically as part of the auto load process, and the parameters get provided from without. The first parameter is the mapping that contains all of the auto load information, and the second contains the player for whom the inventory is being loaded.
void init_dynamic_arg( mapping map, object ob) { if (map["::"]) { ::init_dynamic_arg(map["::"], ob); } if (map["motif"]) { set_motif(map["motif"]); } }

In this first part of this code, we check to see if the mapping we have been provided has anything associated with the key ::". We know it does that key contains all the information from the parent object, and so we call init_dynamic_arg on the parent passing only those parts of the mapping we got from it. Motif is something we handle internally, so we check to see if one has been set, and if it has we call set_motif with whatever was in the mapping. The fact that auto loading is a complex and tricky concept at the deep levels of the mudlib doesn't impact on your code especially. You just need to honour two rules in your objects: Make sure you honour the chain of invocation Store and restore your own state

If every object does this, then auto loading works like a charm and no-one has to know more than how to handle their own object state. It's really very clever when you think about what the alternatives would be.

378

Epitaph Online

http://drakkos.co.uk

Doing It Better
The above is how the majority of objects on the MUD handle auto loading. You'll see it a lot, so it's important you understand how it works. However, there's a better way that leads to much more readable, maintainable code we use the add_auto_load_value and the query_auto_load_value functions to achieve our nefarious aims. The first function, add_auto_load_value lets us add a piece of data to the mapping:
map = add_auto_load_value(map, "zombieville skirt", "motif", query_motif());

The first parameter is the mapping we're working with,the second is a unique identifier for this object. The third is the name we'll set as the key of the mapping entry, and the last is the value itself. Thus, our method should look like this:
mapping query_dynamic_auto_load() { mapping map; map = ::query_dynamic_auto_load(); map = add_auto_load_value(map, "zombieville skirt", "motif", query_motif()); return map; }

And then we use init_dynamic_arg to put them back in place, like so:
void init_dynamic_arg( mapping map, object ob) { string tmp; ::init_dynamic_arg (map, ob); tmp = query_auto_load_value (map, "zombieville skirt", "motif"); set_motif (tmp); }

You can see right away that the code is more readable, but there is an additional benefit if every object in an inherit tree is using this system, what you get out the function is something that can be read and understood by a human. Alas, we don't use it all the way through the mudlib yet, and so results may vary from region to region. Nonetheless, this is how you should be handling your auto loading.

Back to our Shop


We can make our skirts available in the shop easily enough by using our normal scavenge architecture - but when they get searched up, they won't have any motif at all. We can handle this though by defining a function in our shop that does this 379

Epitaph Online

http://drakkos.co.uk

for us. Whenever an item is searched up, the function item_search_callback is called on that item. We can override that to alter the items that come out of the search system:
object item_search_callback (object ob, object player) { string *clans = ({"MacGregor", "Stuart", "Robertson", "Carnegie"}); ob = ::item_search_callback (ob, player); ob->set_motif (element_of (clans)); return ob; }

This will call the set_motif function on anything that gets found in the room - it's possible, if we have multiple items, some of which we cannot configure with set_motif, that this is not the desired behaviour. We can handle that too by checking to see whether or not motif setting is possible using the function_exists efun to say whether or not a function is available in our object:
object item_search_callback (object ob, object player) { string *clans = ({"MacGregor", "Stuart", "Robertson", "Carnegie"}); ob = ::item_search_callback (ob, player); if (function_exists ("set_motif", ob)) { ob->set_motif (element_of (clans)); } return ob; }

There are other ways too of handling this, and it doesn't actually do any harm if you don't have a check like this - calling a function that doesn't exist on an object will simply result in nothing happening. It's a worthwhile efun though, and it's yet another arrow in our quiver of LPC skills.

Conclusion
Having items that can hold their state information is one of the most important things to be able to do when creating interesting code. Almost anything of any kind of complexity has a state that gets manipulated as the player works with it, and being able to store that state between logins is mandatory there's no way to work around it other than resetting the state to some kind of starting value each time. That's bad for all sorts of reasons. The auto load system is complex and intricate, and it requires all objects in the inherit chain to play nicely with all other objects. When that happens though, it's extremely flexible and puts the responsibility for proper restoring and storing of state information in the hands of individual creators, where it belongs.

380

Epitaph Online

http://drakkos.co.uk

Cause and Effect


Introduction
In the last chapter we looked at spells, but rather simple spells. Often spells act as a kind of delivery engine for an effect rather than something that instantly delivers a payload. In this chapter we are going to discuss the idea of an effect, how it can be used, and what it can do. Once again, we start by adding a new define to our path.h file:
#define EFFECTS ZOMBIEVILLE + "effects/"

We are going to address the path that leads to our library in this chapter as you'll undoubtedly remember from Being A Better Creator, this is supposed to be hidden. That's no problem, but we're going to make the searching a little more interesting than we have done in the past.

What's An Effect?
The easiest way to think of an effect is as a temporary set of conditions that impact on a player or an object. For example, an effect may be a disease, or a curse, or a buff, or any number of other things. They get attached to objects on a conditional basis they may last until a certain condition is met, or until a certain period of time has passed. While they are attached to an object, that object can be impacted on an ongoing basis. In the Olden Days, effects often used to come as a pair with a thing called a shadow. A shadow is an object that can be attached to another object and works like a temporary mask on a function. We might have a shadow that overrode adjust_xp to give double the experience it would normally give, or overrode query_skill_level to simulate an object that gave bonuses to certain skills. We don't do that any more that particular mudlib feature is now considered to be deprecated. You'll still see a number of shadows around and about because we haven't gotten them all out of our code. No new shadows should be coded. However, that doesn't mean effects are useless far from it. What we're going to do in this chapter is build an effect that acts as damage over time' system on a player. As a result of searching for the hidden path, they'll get scratched and damaged and bleed for a while. It's a simple effect, but that that will illustrate a good deal about how they are put together. Effects don't need to inherit anything they exist as self-contained sets of code. 381

Epitaph Online

http://drakkos.co.uk

However, they must implement five functions to work properly.


#include <effect.h> int beginning( object player, mixed args, int id ) { } int merge_effect( object player, mixed old_args, mixed new_args, int id ){ } void end( object player, mixed args, int id ) { } string query_classification() { } void restart( object player, mixed args ) { }

The first of these is beginning, and it gets called when an effect is first added to another object. The merge_effect function is called if an effect is added to an object that already has the same effect active. The end function gets called when the effect is removed from an object. Restart is called when a player logs off and then on again. Finally, query_classification provides an 'identifier' code. Let's start by filling in a placeholder for each of these functions and discussing what their parameters do.
int beginning( object player, mixed args, int id ) { tell_object (player, "Argh! You must have put your hands in some " "stinging nettles.\n"); player->adjust_hp (-1 * args); return args; }

The first parameter is the object on which the effect has been applied it doesn't have to be a player, but since this particular effect will indeed be applied to players, we may as well indicate that with the parameter name. When we add an effect to an object, we can provide some arguments to it for configuration purposes. Within the beginning function, they are accessible as the second parameter. The third argument you don't need to worry about. When we return a value from this function, the value we return becomes the argument' of that effect we can access that later in code, which we will come back to later. For now, all this effect does when it is applied to a player is do an amount of damage equal to the argument we pass it. The end function doesn't have to do anything, but it's always called when an effect is removed and it can be a good way for us to provide some information to the player reflecting that the effect has come to an end. The parameters are the same as for beginning:
void end( object player, mixed args, int id ) { tell_object (player, "The stinging from the nettles subsides.\n"); }

382

Epitaph Online

http://drakkos.co.uk

For query_classification, we give some meaningful identifier for this effect. It doesn't really matter what it is, but for convention we usually use something like body.nettles' to give a fair idea of what it is and where it comes from:
string query_classification() { return "body.nettles"; }

By default, if two effects are added to the same object, the object gets two copies of an effect running on them. Usually we don't want that, and so we override that behaviour with the use of merge_effect. Whatever we return from this function becomes the new value of the argument of the effect. Let's make it so that we simply use the stronger of the two:
mixed merge_effect( object player, mixed old_args, mixed new_args, int id ) { if (old_args > new_args) { return old_args; } return new_args; }

Restart gets called each time the effect gets restarted, so let's use it to supply a message to the player:
void restart( object player, mixed args ) { tell_object (player, "Argh! You must have put your hands in some " "stinging nettles.\n"); }

And that, believe it or not, is an effect. It's not much of one, but it's an effect nonetheless. We can test it out by using the add_effect method, like so:
> call add_effect ("/w/your_name_here/zombieville/effects/nettles", 100) me Argh! You must have put your hands in some stinging nettles.

Along with that message comes 100 points of damage, as we planned all along. To get a list of all the effects on an object, I can use the effect' command:
> effect drakkos Effects on Drakkos: [0] body.wetness (1043) [1] body.nettles (200)

In square brackets before each of these effects is a numeric identifier, the effect number (or enum for short). Here we can see the effect with the enum 1 is body.nettles, which is what we gave it for a classification. The 100 in brackets indicates what the argument of that effect is. This effect will remain in place until I die, or I delete it like so:
> call delete_effect (1) me The stinging from the nettles subsides.

I can add this effect as many times as I like to myself, and I will only ever have one body.nettles effect on me merge_effect deals with that. 383

Epitaph Online

http://drakkos.co.uk

Since this is an effect that has a visible player impact, we want it to show up using the debuffs command so that people know they are being affected by it. To support this, we need to define three additional methods in our effect:
string query_buff_name() { return "stung by nettles"; } string query_buff_description() { return "Your hand is throbbing - you must have stuck it in some " "nettles, silly billy."; } int query_debuff() { return 1; }

Now it'll show up properly when we check our debuffs:


> debuffs stung by nettles Your hand is throbbing - you must have stuck it in some nettles, silly billy. soaked to the skin You are cold, and miserable. This has no real game impact, but it's a little annoying.

To make it show up with the buffs command instead, we simply omit the query_debuff function, or make it return 0. So that's an effect it's simple, but not exciting. Let's open this puppy up though and see what we can do.

The Power of Effects


The real power of effects comes from how easily we can cause our code to have certain functions called at regular intervals. With our nettle effect, it just does the damage once and that's it. If we want damage to also be done on a periodic basis, we can do that using the submit_ee (ee is short for 'effect event') function on our player. This function takes three parameters the first is the function to be called when the effect event occurs. The second is the interval between the ticks of the event, and the third is how often the event should be submitted. This should be one of three values, as defined in effect.h: Effect Interval EE_REMOVE EE_ONCE EE_CONTINUOUS Description Call it once, and then remove the effect from the player when it has occurred. Call it once, but leave the effect intact Call it repeatedly, leaving the tick period between invocations

384

Epitaph Online

http://drakkos.co.uk

So if we wanted damage done continuously while the effect was active, that's very easily done the function we tell the effect to trigger comes with the same parameters as for beginning and end:
mixed beginning( object player, mixed args, int id ) { tell_object (player, "Argh! You must have put your hands in some " "stinging nettles.\n"); player->adjust_hp (-1 * args); player->submit_ee( "damage_me", 20, EE_CONTINUOUS ); return args; }

This is the code that starts the effect timer running, and then we simply implement our damage_me function:
void damage_me(object player, mixed args, int id ) { player->adjust_hp (-1 * random (args)); tell_object (player, "Oooh, that stings!\n"); }

Every twenty seconds now, we'll trigger the damage_me function and that will do a random amount of damage to the player based on how large an initial argument we set. It will do this until the player dies, which is perhaps not exactly fair. To make it a little less cruel, we combine this with a submit_ee that has the interval set to be EE_REMOVE. This doesn't even need to have a function, it'll just cause the effect to be automatically removed when the tick period has passed.
mixed beginning( object player, mixed args, int id ) { tell_object (player, "Argh! You must have put your hands in some " "stinging nettles.\n"); player->adjust_hp (-1 * args); player->submit_ee( "damage_me", 20, EE_CONTINUOUS ); player->submit_ee( 0, args, EE_REMOVE); return args; }

Now we have an effect that is time limited by the initial argument too. If we wanted, we could make the argument more granular so that we added an array instead of a single integer, but the theory is the same. Now the last thing we need to do to this effect is make it merge properly. It's already handling the possibility of a player being affected by a stronger version of the effect (remember, whatever we return for merge_effect becomes the new argument of the effect), but we should also make sure it impacts properly on the duration. That's easy to do with the expected_tt function, like so:
mixed merge_effect( object player, mixed old_args, mixed new_args, int id ) { int time_left = player->expected_tt(); time_left += new_args; player->submit_ee (0, time_left, EE_REMOVE); if (old_args > new_args) { return old_args; } return new_args; }

385

Epitaph Online

http://drakkos.co.uk

Only one EE_REMOVE is ever active on a player for a particular effect, so when we submit our new one we overwrite the old one. Now, each new application of the nettles effect will increase its duration, but not cumulatively impact on its strength. For a real effect, we probably don't want this either (some set ceiling is usually appropriate, although we don't always honour that), but it'll do for us. The next step is to write some code that adds this simple effect to a player. We go to Zombieville_02 to do this. First we modify the exit to the trail so it isn't obvious, and set up the rest of the things we need:
#include "path.h" inherit INHERITS + "outside_room"; void setup() { set_short ("skeleton room"); add_property ("determinate", "a "); set_long ("This is a skeleton room.\n"); set_light (100); add_item ("brush", "It looks unpleasant, full of nettles and " "other horrible stinging things."); add_item (({"nettle", "horrible stinging thing"}), "You wouldn't " "want to take a widdle in that lot."); set_light (100); add_exit ("north", ROOMS + "zombieville_03", "road"); add_exit ("southwest", ROOMS + "zombieville_01", "road"); add_exit ("southeast", ROOMS + "zombieville_08", "road"); modify_exit ("southeast", ({"obvious", 0})); }

We're going to handle the searching a little differently here too, because we want something that is unique to happen - our framework for scavenging is for when we want to find items, and that's not what we want to do here. We want an exit to be revealed based on the result of our searching. Luckily, that too is easy to do we put a function called do_search in our room, and the searching system will do the rest for us:
int do_search (string str) { int found; int success; success = TASK_MAPPER->perform_task (this_player(), TASKMAP_NOTICE_SOMETHING, TTSM_DIFF_INCREDIBLY_EASY, 0, TM_FREE); switch (success) { case TASKER_AWARD: tell_object (this_player(), "%^YELLOW%^%^BOLD%^You feel a little more " "perceptive.\n%^RESET%^"); case TASKER_SUCCEED: tell_object (this_player(), "You see a trail leading through " "the brush to the southeast.\n"); found = 1; break;

386

Epitaph Online case TASKER_FAIL: found = 0; break; default: } if (random (2)) { this_player()->add_effect (EFFECTS + "nettles", 100 + random (100)); } if (found) { return 1; } else { return -1; } }

http://drakkos.co.uk

All of this should be pretty straightforward at this point - the only thing that is new is the return values of the function. 1 indicates 'this search was successfully handled, thanks', and -1 indicates 'Nothing was found, go on with the rest of the normal search'. If you return a 0, it won't do the normal search messages so you can provide your own. It is the add_effect method that provides us with a mechanism for applying the effect, and that method is common to all objects that inherit /mudlib/inherits/object along the way. As such, effects can exist on inanimate objects as easily as they can on players, and the mechanisms for dealing with them are identical.

Working with Effects


Coding effects is one part of the problem. The next is how we can work with effects that are already on players. For example, we may wish to have different courses of action available to people who are afflicted with particular effects. The first thing we commonly want to do is determine if a player has a particular effect on them. We do this with the effects_matching function to which we pass the classification of effect for which we want to look. As a result, we get an array of all the matching effect numbers (enums, remember), or an empty array if no effects match the classification. Let's go back to our search function let's tell players afflicted with our effect that it hurts too much for them to search in the room:
int do_search (string str) { int found; int success; int *effects; effects = this_player()->effects_matching ("body.nettles"); if (sizeof (effects)) {

387

Epitaph Online

http://drakkos.co.uk

tell_object (this_player(), "You are still stinging from your " "brush with the nettles, and you can't bring yourself to search " "too effectively.\n"); return 0; } // Code as usual }

The next thing we often want to do is remove effects based on some external factor. An old folk remedy to nettles claims that the plant 'horsetail' is effective for removing the stinging sensation. Let's add some horsetail here and have its impact be to remove the effect if it exists. We use delete_effect to handle that. First, the add_item:
add_item ("horsetail", (: horse_tail_function :));

And then the function that does the magic:


string horse_tail_function() { int *effects; string ret; ret = "Yes, there is some horsetail here. ";

effects = this_player()->effects_matching ("body.nettles"); if (sizeof (effects)) { ret += "Throbbing from the stinging nettles, you clutch it " "thankfully."; for (int i = 0; i < sizeof (effects); i++) { this_player()->delete_effect (effects[i]); } } return ret; }

Note that we delete the effects in a loop remember an effect without a merge_effect will result in more than one effect being applied to the object, and we can't guarantee that the effects we are working with have only one instance set on them. Whenever deleting an effect then, we do it properly we get rid of all of them. Now, the thing about folk cures is that they are horribly ineffective. Really, our horsetail should only reduce the impact of the effect, rather than remove it entirely. To do that, we need to know what the value of the effect actually is. We can get the current argument with arg_of, and we can change its value with set_arg_of. Both of these need us to provide the enum of the effect we wish to query or modify, like so:
string horse_tail_function() { int *effects;

388

Epitaph Online string ret; int val; ret = "Yes, there is some horsetail here. ";

http://drakkos.co.uk

effects = this_player()->effects_matching ("body.nettles"); if (sizeof (effects)) { ret += "Throbbing from the stinging nettles, you clutch it " "thankfully."; for (int i = 0; i < sizeof (effects); i++) { val = this_player()->arg_of (effects[i]); val = val - 25; if (val <= 0) { this_player()->delete_effect (effects[i]); } else { this_player()->set_arg_of(effects[i], val); } } } }

For complicated effects, the arguments may well be more complex than just an integer, but the principle is identical. The arg_of function gives us what the argument of the effect is, and set_arg_of allows us to change it.

Bits and Pieces


There are some other details that we need to talk about before you're ready to make use of effects properly. The first is when you're writing an effect, you cannot use this_player. It is hardly ever going to be what you want it to be. Instead, use the object reference provided as a first parameter to all the methods this ensures that you're working with the object to which the effect is attached. Sometimes we want effects that exist indefinitely it's entirely possible that we may want an effect that doesn't have any persistent behaviour associated with it (as in, no events get scheduled). The effects management code can detect effects that have no pending events, and will often remove them. If we are creating a genuinely indefinite effect, we need to stop it doing this by adding a query_indefinite function:
int query_indefinite() { return 1; }

Upon death, all effects are removed from a player. If we have an effect that should persist through death, we also include a survive_death function:
int survive_death() { return 1; }

389

Epitaph Online

http://drakkos.co.uk

If we are creating an effect that a player may be able to cure (with a spell, ritual, or such), we can define a test_remove function to control whether our effect is actually impacted. It takes four arguments the object on which the effect is active, the argument of the effect, the ID of the effect (ignore this), and the bonus with which you should work with to see if an effect should be removed. If we return a non-zero value, the effect gets deleted when the appropriate trigger condition occurs. If we return 0, the effect remains in place:
int test_remove(object player, mixed arg, int id, int skill_bonus) { if (skill_bonus > arg) { return 1; } return 0; }

Finally, remember that many of the effects that you will read examples of also use shadows. We don't do that kind of thing any more shadows were very powerful, but came at considerable cost to the MUD's efficiency. If there is functionality you want to create that would require a shadow object, don't go ahead and write one instead, consult a more experienced creator who might be able to put forward an alternate course of action.

Conclusion
Effects are a tremendously useful way of creating temporary conditions that get attached to objects in the game. With then we can create all kinds of sophisticated behaviours such as poisons, buffs, debuffs, and all sorts of things in between. Here we have created a relatively simple effect, but with a little modification it would be possible to change it into a powerful heal over time' spell, or a more comprehensive damage over time' that incorporated all sorts of deleterious effects. Have a read over what we have available in /mudlib/effects/ and see the kind of things that you can do with a little bit of imagination.

390

Epitaph Online

http://drakkos.co.uk

Achieving Your Potential


Introduction
Achievements are a core mechanism for Epitaph MUD - a way to get an extra degree of 'oomph' out of your code without you needing to actually write anything new. Achievements give you a way of focusing attention on the unique features of your development, and rewarding those players who participate most fully in the gaming experience you have provided. Unlike quests, an achievement is very simple to write, and often requires very little code beyond a few lines to update a counter or trigger a comparison. Unfortunately, achievements are not things that can be integrated into Zombieville because of the way they are made active in the achievements handler. All we can do in this chapter is go over the concept. This is then largely a self-contained chapter on how to use our achievements system, with no reference to Zombieville. Indeed, that's how the chapter first existed when it was just a document on a webpage.

Achievements and Quests


So, what is an achievement? For those who are not familiar with these in other games, they may seem just another way of adding quests to the game. This perception is especially muddled by the fact we have had no consistent conceptual architecture when we were developing quests in the past. We have always operated, vaguely, under the guiding principle that a quest should be a puzzle it is this principle that underlines most of our framing of the rules for quests. Achievements are rewards for participating and excelling in the game. They are publicly announced (when they are above a certain level), and available for browsing. They have no requirement to be IC they can be a combination of IC and OOC elements. They can be for achieving certain benchmarks in the game (hitting certain guild level targets), or for passing some threshold of participation (behead 100,000 people as an example) However, we have numerous quests that aren't actually puzzles in any sense. For example, we have a quest to send a certain number of flowers, another to buy a certain number of town crier messages, and so forth. These would correctly be implemented as achievements (except they predate achievements by Quite Some Time), and it is largely the existence of quests like this that muddy the definitional parameters. So, let's outline our philosophies on these two gameplay mechanics. Quests are: 391

Epitaph Online

http://drakkos.co.uk

Puzzles Entirely in-character Subject to our rules on secrecy and spoilers

Achievements are: Participation incentives A combination of IC and OOC elements In the public domain for discussion

And, let's not forget that, from a development perspective, achievements are tremendously easier to implement than even a simple quest.

How Often Should I Add Achievements?


I would like people to be pretty free in handing out achievements. They are an ideal way of focusing attention on game features that may not have had a lot of publicity, a great way of providing XP to newbies, and an equally good way of rewarding people for playing in ways that do not get proportionately rewarded by our rather combat heavy advancement system. However... We don't want to be ridiculous about it. There is a temptation to put in a dozen achievements for each minor development to make sure people pay enough attention. That alas just devalues the rest of them. Imagine someone for example adding a street with five shops, each with a small achievement to go with them where the achievement is 'Buy $X of stuff from this shop'. That's a pretty transparent attempt to curry interest. Instead, a single achievement of 'buy $X of goods from these shops' is more appropriate. Even then it's questionable, since it's not really a fun achievement. For complex subsystems in a development you may want to add one or two achievements specifically focused on that subsystem directly, in addition to the general area achievements. So you may have 'be punished for every faction crime' as an achievement to encourage participation in your subsystem. Lord knows we have enough subsystems that don't get used enough. Basically, I'm saying that because APs are so easy to give out, we should have a bit of discipline in what we chose to give them for, and not overdo it. Achievements should actually mean something if they are to be worth the name 'achievements'

392

Epitaph Online

http://drakkos.co.uk

My First Achievement
Okay, let's start developing our first achievement. To begin with, we need to become familiar with the format of an achievement file (a .ach file). These are located in one of two places: /data/achievements/, for achievements that are mud-wide /d/<domain>/achievements/ for achievements that are domain specific.

For achievements within a particular domain, it is the domain administration of that domain who has the authority for approving or rejecting achievements. For /data/achievements/, you should give me (Drakkos) a nudge. If I am not available, find a fellow creator with sufficient seniority (the rule is if you have to ask if you have the seniority to approve something, then you don't have it). Let's take a look at a simple achievement - one for killing ten zombies:
::item "killing:plight of the living dead":: ::->name:: "plight of the living dead" ::->story:: "put a few of them down" ::->level:: ACHIEVEMENT_MINOR ::->criteria:: ([ ({"killed zombie", "Zombies killed"}) : 10 ]) ::->category:: ({"killing"}) ::->instructions:: "This achievement is awarded upon killing 10 zombies " "of any type."

The first part of the achievement (the 'item' bit) is used internally in the data compiler to build a mapping of achievements in a particular domain. You don't need to worry about this at all, just as long as you're sure that this value does not duplicate anything elsewhere in the system. By convention, we use the domain responsible followed by the name of the achievement for this. This is an internal value nobody ever sees this. The rest of the information you set is in the public domain it'll all be viewable by players in one form or another. Name is the title your achievement is going to have this is what will appear when people browse or achieve it. Story is the little 'jokey' description of what people did to get the achievement. This one, for example, will appear in the player's achievements list as:
Plight of the living dead, in which you put a few of them down.

You do not put in the 'in which you' bit that's added automatically to provide a measure of consistency with the stories we use for quests. The level is how much of an achievement this actually is the values this can be set to are defined in achievements.h. This directly impacts on the amount of XP an achievement awards.

393

Epitaph Online

http://drakkos.co.uk

We'll come back to the criteria bit it's the most important part of your achievement file. Category is, unsurprisingly, what categories this achievement falls under. These are viewable on the website or in the MUD using the 'achievements' command. Finally, the instructions contain the detailed explanation of the achievement. This is available through the website or through the 'achievements details' command. Our philosophy is that there is no secrecy on achievements, so be as detailed as you can with this. You should be familiar with the format by now - it's just a data file, like so many files on Epitaph.

Criteria
Okay, but what about that criteria part? That looked a bit more complicated! The criteria is the thing that drives your achievement it's the bit that tells the achievements handler whether or not a player has passed the threshold you have set for it. Each player, via the achievements handler, has a multi-purpose mapping of well, let's call them 'criteria values' that you can manipulate via code. There is no restriction on these you can call them whatever you like. The criteria of an achievement is set as a mapping of criteria values and the thresholds they must have for the achievement to be granted:
::->criteria:: ([ ({"killed zombie", "Zombies killed"}) : 10 ])

The key of this is an array - the first element is the internal value used for the achievements handler, and the second is that the text players will see when they browse the achievement - it's the bit linked to their achievement progress. For this achievement, it is granted when the 'killed zombie' criteria value is ten or greater for a particular player. The achievements handler has no knowledge in advance of what these values are going to be, and it has no idea when this value should be changed this is where the link between your code and the achievements handler is needed. You tell the achievements handler when it is to change this value, and the handler does the rest. You do this through the player function adjust_player_value. To ensure interoperability of game systems such as the faction handler, alignment system, goal handler and so on, we re-use these codes across the game. When you call adjust_player_value on a player, it will update it in all the appropriate handlers. When a player kills a zombie then, there is a line of code like this:
player->adjust_player_value ("killed zombie", 1);

When this is done, all the handlers update their 'killed zombie' counter, and when awards are appropriate, it marks the appropriate thing as completed - when your 394

Epitaph Online

http://drakkos.co.uk

killed zombie value reaches 10, you'll be granted the achievement. That, in its entirety, defines a simple game achievement. An achievement file (the .ach file) and the code that adjusts the value appropriately. Absolutely everything else is done for you.

Criteria Matching
Criteria are set as a mapping because you can have multiple different values that need to be hit for an achievement to be awarded. For example, we could have the following:
::->criteria:: ([ ({"killed zombie, "Zombies Killed"}) : 5, ({"killed survivor", "Survivors Killed"}) : 10, ])

With this as a criteria, the player must have the value of five or greater for their killed zombie value, and ten or more for their killed survivor value. This may not be exactly what you want you may wish to provide two or more ways to gain this achievement. The default matching routine for criteria is AND they get the achievement if they meet all of the criteria values. However, you can also set it to use an OR criteria (they get it if they match any of the criteria value) by adding this to your .ach file:
::->match_criteria_on:: CRITERIA_OR

Alas, you cannot mix and match these (you can't do a compound achievement in which you have to do A and B or C) if you're getting into that kind of complexity, it's probably becoming more quest-like than anything else. You may also wish to make use of arrays within your criteria. For example, let's say you wanted an achievement that added a name to an array each time you ate a corpse, and you wanted to grant an achievement when someone had eaten the right four people. You can do this in your criteria:
::->criteria:: ([ ({"Right people eaten", "people eaten"}) : ({"drakkos", "eek", "hambone", "ploosk", "haug"}) ])

And then when it comes time to adjust the player's achievement values, rather than using adjust_player_value, you use instead add_player_value_array.
player->add_player_value_array ("people eaten", "drakkos");

Arrays in an achievement have a maximum size, based on the largest comparison size set in related arrays this is to keep the memory requirements down and to make sure people don't try to keep an array of every room a player has visited for 395

Epitaph Online

http://drakkos.co.uk

their 'visit every room in the world' achievement. The size of an array will default to ten, and if you start adding to an achievement array that already has the maximum number of elements, it will lose the first one and add the new one to the end. By default, this method will not allow duplicates in an array. If the method call above was called three times, there would still only be an array containing the name "drakkos". You can force it to have duplicates by adding a last parameter of 1:
player->add_player_value_array ("people eaten", "drakkos", 1);

Call this three times and you end up with an array that contains the name 'drakkos' three times:
({"drakkos", "drakkos", "drakkos"})

You can also remove things from the array with the remove_player_value_array call:
player->remove_player_value_array ("people eaten", "drakkos");

This will remove one instance of the value, but you can force it remove all instances by adding a 1 to the parameter list;
player->remove_player_value_array ("people eaten", "drakkos", 1);

By default, the handler will attempt to match on array contents so the player's criteria value has to have in it all of the elements indicated by the criteria. However, you have two other options you can match on the size of the arrays by adding the following to your achievements file:
::->compare_arrays_with:: CRITERIA_SIZEOF

Or you can check to see if the player's array contains a specific value:
::->compare_arrays_with:: CRITERIA_MEMBER_ARRAY

Let's look at how that would work in different situations:


::->compare_arrays_with:: CRITERIA_ARRAY_MATCH ::->criteria:: ([ ({"Right people eaten", "people eaten"}) : ({"drakkos", "eek", "hambone"}) ])

This achievement will be granted if and only if the array associated with the 'people eaten' value contains the four specific names mentioned.
::->compare_arrays_with:: CRITERIA_SIZEOF ::->criteria:: ([ ({"Number of people eaten", "people eaten"}) : 10 ])

396

Epitaph Online

http://drakkos.co.uk

This achievement will be granted if the people eaten array contains ten elements. It doesn't matter what those elements are.
::->compare_arrays_with:: CRITERIA_MEMBER_ARRAY ::->criteria:: ([ ({"Right person ate", "people eaten"}) : "drakkos" ])

This achievement will be granted if and only if it contains the value 'drakkos'. What about if we mix it up a bit?
::->match_criteria_on: CRITERIA_AND ::->compare_arrays_with:: CRITERIA_MEMBER_ARRAY ::->criteria:: ([ ({"Right people eaten", "people eaten"}) : ({"drakkos", "fole", "garrion", "eek"}) ({"Right amount of people eaten", "number of people eaten"}) : 100 ])

This achievement will be granted only if the people eaten array contains the four specific names and the number of people eaten criteria is 100 or higher. You can create some pretty sophisticated achievements in this way by mixing and matching criteria. Alas, you can only set one way of comparing arrays as of the time of writing anything more complicated is once again verging into territory best explored by quests.

A Little More Complicated


Ah, you may say a lot of the achievements in the game are more complicated than that! This is true, but these are achievements that hook into game handlers or call functions on objects to generate their criteria values. For most achievements, they should absolutely be as simple as indicated above. However, there is nothing to stop you using a function pointer as your criteria if you want to do something a little more complicated. However, if you do this it needs you to trigger the checking of achievements a little differently. When you provide a specific criteria value (such as 'killed zombie'), the achievements handler can work out which of these belong to which achievements. However, if a criteria is a function it has no way of knowing what achievements can belong to that pointer. If the function pointer references an external object (such as a handler), the achievements handler likewise has no way of knowing when the internal state of another object changes. Thus, we need to explicitly tell the achievements handler when the internal state of other objects have been modified. 397

Epitaph Online

http://drakkos.co.uk

Note this_player() in an achievements criteria is a no no. Achievements can be checked while people are not online, or through the web, or in situations where this_player is 0. Likewise, find_player is not to be used in achievement criteria for very similar reasons. If you wish to check the value of a function defined on a player, you can either use the player handler (which has a number of useful methods), or the method call_function_on_player in the achievements handler this will ensure the player is online and return the cached value of a criteria if no player can be found. Please, do not attempt to call functions on players directly! The call_function_on_player method takes two parameters - the name of the player, and and array containing the function to call and any parameters that go along with it. Let's look at a simple example of an achievement using a function pointer:
::item "mudlib:stayed the course":: ::->name:: "stayed the course" ::->story:: "stayed the course, you held the line, you kept it all together." ::->level:: ACHIEVEMENT_MINOR ::->creator:: 1 ::->first_person:: "maestro" ::->criteria:: ([ (: (time() - ACHIEVEMENTS_HANDLER->call_function_on_player ($1->name, ({"query_character_start"}))) / (60 * 60 * 24) :) : 180, ]) ::->category:: ({"social", "age"}) ::->instructions:: "This achievement is awarded upon having a player account " "of six months or older. If you have refreshed, the time starts " "counting from the time of your most recent refresh."

All of this is stuff we've talked about before, with the exception of the criteria:
::->criteria:: ([ (: (time() - ACHIEVEMENTS_HANDLER->call_function_on_player ($1->name, ({"query_character_start"}))) / (60 * 60 * 24) :) : 180, ])

That is a pretty dense line of code, so let's extract bits of it so that we can see what's going on. First, let's look at the key and the value of the criteria mapping: Key
(: (time() - ACHIEVEMENTS_HANDLER->call_function_on_player ($1->name, ({"query_character_start"}))) / (60 * 60 * 24) :)

Value 180

Now, the (60 * 60 * 24) calculation gives us a number that is (roughly) equivalent to the number of seconds in a day, so we can unroll this function a bit farther: Key Value 398

Epitaph Online (: (time() - ACHIEVEMENTS_HANDLER->call_function_on_player ($1->name, ({"query_character_start"}))) / 86400 :)

http://drakkos.co.uk

180

The call_function_on_player says 'hey, call query_character_start on the player if they are online, or give me back their last cached value if they are not. For me, that value is 1261078145, so let's slot that in: Key
(: (time() - 1261078145 / 86400 :)

Value 180

If the result of this evaluation is greater than or equal to 180, then the achievement will be granted. The first parameter passed to the function pointer is the player details class relating to the player we are checking. The full definition of this class is in achievements.h, but the name parameter of this is usually all you'll need:
$1->name

You may find upon doing this that you get an error relating to achievements not being disambiguated. Don't worry about what this means you fix it by adding this fairly ugly piece of code;
((class player_entry)$1)->name

You may only get this error if you are using classes in a header file used by the achievements system. Anyway, that's an achievement using function pointers. However, the achievements handler has no way of knowing when it should check this, so checking it has to be triggered elsewhere. There are two ways of doing this. The first is for when achievements belong to a common category and should all be checked at the same time for example, the MUD continually (on a delay) updates how long you've been online. When we do that, we say 'And now you've done that, check to see if this now warrants an achievement:
ACHIEVEMENTS_HANDLER->check_achievement_by_category ("player name", ({"age"}), 0);

The 0 refers to whether or not these are achievements being granted by the legacy command if it is set to 1, it will suppress all informs except those that go to the player. If you have a smaller subset of achievements you want to award, you can check specific achievements:
ACHIEVEMENTS_HANDLER->check_achievements ("player name", ({"achievements", "to", "check"}), 0);

These calls go wherever the internal state of the object being checked changes 399

Epitaph Online

http://drakkos.co.uk

so for the age ones, they go into the player heart beat (although they don't get triggered every heart beat), for the faction reputation ones they go wherever the state of faction standing is altered, and so on.

Other Achievement Settings


Titles can go along with achievements, but these (with rare exceptions) should go along with achievements of the LARGE level or higher. Check with your domain administration to see whether or not your achievement can grant a title. All you have to do to provide a title is to indicate it in your achievement file:
::->title:: ({"awesome"})

f you want to temporarily switch off an achievement, use the inactive flag. Don't delete the achievement itself, it won't make as much of a difference as you'd like:
::->inactive:: 1

If you want to set the achievement as first obtained by Maestro (which we do when we can't know who was the first person to get it), then we do the following:
::->first_person:: "Maestro"

If it's in playtesting, then:


::->playtester:: 1

If you want to give a custom message to the player getting the achievement:
::->message:: "You rock!\n";

And if you want to register a callback for when an achievement is completed:


::->callback:: (: load_object (MY_HANDLER)->register_awesome ($1->name) :)

There are other settings too, but at this point you are more than competent enough to go hunting for that functionality yourself.

I've written an achievement! What now?


Congratulations! So, if you're sure you've got permission to make the achievement active, all you do is save your .ach file into the appropriate directory (/data/achievements/ for mudlib achievements, and the appropriate achievements sub-directory of another domain if it's domain specific). Then, you use the rehash command to make that achievement active - the rehash command will make sure that the achievement works and won't destroy the 400

Epitaph Online

http://drakkos.co.uk

handler (which a badly coded achievement can do):


rehash achievements

If you get an error message doing this, you need to fix your achievement before making it live. A second or so later, there will be an announcement to the MUD indicating that a new achievement has entered the game. Then you just sit back and bask in the loving, warming glow of the warming love of our player-base.

Ack, it sucks! How do I get rid of it?


You may have tried to delete the .ach file and found it didn't make much difference that's because the handler stores details about your achievement so that it can make a note of when it is achieved and how many times and so on. If you are sure you want that achievement to go away, you can do the following call:
call clean_achievement ("my achievement") /mudlib/handlers/achievements_handler

And bam, away it will go!

Achievement Levels
It's not necessarily easy to work out what level your achievement should be, because there are all sorts of factors. The rough mapping I use to determine the level is as follows: Achievement Levels Minor Small Points 1 2 Notes Give out like candy. At most, thirty minutes of effort to obtain. An hour or so of effort dedicated to the achievement, or a longer period of time if the achievement is granted by doing what people would do anyway. Killing 100 o a specific kind of NPC might take an hour or so to do and be a small achievement, but getting a common skill to level 150 might take much longer but it's something you'd be doing anyway Half a day or so of effort. A day or two of effort Three or four days of sustained effort Several weeks of sustained effort A year of sustained effort Several years of sustained effort A decade of sustained effort

Moderate Large Huge Massive Epic Legendary Mythic

3 4 5 6 7 8 9

Time invested is not a straight-jacket and very difficult to measure. It's just 401

Epitaph Online

http://drakkos.co.uk

roughly how big an effort you can realistically expect of people when setting your criteria. You also need to factor in things like cost in money, cost in skills, risk, etc, etc. Feel free to nudge me if you want some suggestions on where a particular achievement should sit. As to the level, feel free to adjust it up or down as needed, although if you're adjusting it two or more levels you should consider why you feel the need to do that. Have a solid reason like 'This is really risky', or 'it loses someone months of faction reputation', or 'it's moderate even though it takes a year, because it's an achievement you work towards even if you're not paying attention'. For example, login time is something you accrue regardless and is an achievement in the system, as is the time since your first login. Despite the guideline being a decade for mythic, it actually needs 20 years to have passed for 'Dawn of the World' to be achieved because the time passes without you doing anything

Conclusion
Achievements are a powerful game-play mechanic, and they have a tremendous impact on the kind of game that we have. From a creating perspective, they excel at adding value to the code that we have with a minimum of creator effort. It's well worth spending a bit of time thinking where you could enhance your domain by drawing people to the right kind of locations through achievements.

402

Epitaph Online

http://drakkos.co.uk

So Here We Are Again...


Introduction
I guess it must be fate. We have reached the end of our journey together. There are no more mountains to climb. No more roads to travel. We must simply embrace and wish each other a long, healthy life. I haven't told you all there is to know about LPC. Oh, far from it. What I have done though, and I hope successfully, is give you the grounding you need to be able to take control of your own learning. Really, that's all I can hope for that as a result of the intimate candle-lit discussions that we have shared, you have in some way learned, perhaps by osmosis, the skills you need to know where to look for further information.

What's On Your Mind?


Zombieville has been quite a complicated development, and it has been tied quite tightly into the discussion of several important programming concepts. It might be worth spending a few moments talking about what you have actually been exposed to in the course of this document. First of all, we learned about inheritance and how it can be used to build a framework for an easily modified development. We made use of this knowledge throughout our code, making sure we had our own bespoke inherits for every kind of object we used. Then we talked about data representation, and the uses of the class data-type. Data representation is incredibly important and you should spend a lot of time thinking about how you approach this in any even moderately complex development. We introduced the topic of add_commands, and the various syntaxes and patterns that go with it. This opens up a world of interaction possibilities within our rooms and objects. This allowed us to develop our first quest, and that in turn led to our discussion of the quest handler and the library handler, as well as the quest utility inherits that can be used to simplify data management. Inherits are one half of a code architecture the other half comes from handlers, and we built one of these in the process of developing Zombieville. We talked about events, and how we can trap these to provide responsive behaviour in our NPCs, and how data persistence can be implemented on the MUD. We also talked about the NPC behaviours, bosses, and boss powers that you can use to make your NPCs throw some interesting attacks out there when they find themselves in combat. 403

Epitaph Online

http://drakkos.co.uk

As part of our knitwear shop, we made an item that had to save its state between logins, and that lead us to the discussion about the auto loading system. We wrote an effect. We learned about achievements, and we talked about function pointers. We've talked about an awful lot in this material, and all of it is important for you to know as a fully rounded Epitaph creator. This does not complete your toolbox there's all sorts of things we haven't talked about. We haven't talked about terrains, or socket programming, or coding for the web - you're well placed now to learn about all of that stuff yourself. There is always more to learn, but now you will not only know enough to code some very interesting objects, you'll also have the necessary background information to be able to understand new and interesting code that comes your way.

Help Us to Help You


LPC is a niche language, and it's not especially well documented. The same thing can be said for large portions of our Mudlib. Here be dragons, as they say. There are all sorts of clever features and little tricks that people know how to do, and every now and again you'll find one that throws you. There's nothing I can do to get around that, except perhaps spend the next ten years of my life writing more and more of this kind of thing. I don't intend to do that. However, there's no reason that you can't help yourself and the people around you as you make your way through the world of being a Epitaph creator. Whenever you discover an undocumented feature, then document it. When you've spent a couple of hours puzzling over a bizarre bug, then tell people about it. We're all in this together don't think that your knowledge is in any way commonplace. You may be the only one who knows what you know, or the only one who spent time working out how that weird situation manifested itself. We have a wiki that is a great place to record little essays on things that you've discovered, and there is always the learning board, or the Epitaph Oracle. More than anything else, if you're doing something strange or complicated, then document it! Better still, do it in a less complicated way. Very few creators are still around from the very first days of the MUD. Along the way, we lost an awful lot of knowledge every day we live with the consequences of that in the shape of design decisions that were made, or strange coding syntaxes that were implemented. In ten years time, the MUD will hopefully still be here you may not be. Part of your legacy should be what you have contributed to the knowledge-base that we collaboratively build.

Where Do You Go From Here?


Now that you've finished with this material, where do you go? What should you be doing next to move on to the next level of being a Epitaph creator? 404

Epitaph Online

http://drakkos.co.uk

I am a big believer that the only way you get better is to set yourself a task that you don't know how to complete. Don't stay inside your comfort zone try to find something new and exciting in every project you work with. There is very little that cannot be achieved within the Epitaph Mudlib and the framework that LPC provides, and the struggle to do things that you have never done before is where your real learning begins. You don't learn how to code from a book, you learn by sitting down and puzzling over a difficult problem. You've got access to a lot of code as a Epitaph creator. Perhaps not access to change it, but you can certainly read code without any difficulty. Reading the huge amount of code we have in the various domains and Mudlib directories will introduce you to new and quirky (and not so quirky) ways of building complex objects. From this point on though, it's all on you. There are several more general resources I can recommend for those who want to become better coders. The language you use is just a frame on which you hang your development all programming is essentially the same thing, at least within particular families of code. Come speak to me if you have specific requests for things you'd like to learn more about.

Conclusion
So long, farewell, adieu. This text has been a long time coming. LPC For Dummies one, the first edition, was written in October of the year 2000. I'd been a creator for about a year and a half. People liked it though, and I was constantly asked about a sequel that would address the more complicated parts of LPC development. I've been promising it ever since then, and I am delighted to finally be able to provide it. My plans for Intermediate LPC changed a lot over the years. Really now it's one of four tightly related works. Together they form a kind of informal Epitaph Creator Manual'. A lot of material that was originally planned for Introductory LPC 2 made its way into other works, and you should consider the these texts to be four separate sections of one larger book. I hope, in your travels, you find it of use to you.

405

Epitaph Online

http://drakkos.co.uk

Section Four

Working with Others


406

Epitaph Online

http://drakkos.co.uk

People Skills
Introduction
I know it sounds horribly touchy-feely we're game developers, not teenagers on a camping trip. However, the most vital skill for life that you'll pick up from being a Epitaph creator is how to work with other people. Absolutely everything we do is a collaborative exercise. In terms of your immediate environment, you are part of a team in your own domain. However, before too long you start to collaborate within the larger context of creatordom as a whole. At that point, your ability to work with others in a large-scale development environment is perhaps your most valuable asset.

Whole New Skills


It's very rare that developers work within as close quarters as we do on Epitaph thus, even those with considerable coding experience are going to find this a largely unique experience. There are things about multi-developer environments you just don't learn until you start working in one. I teach software engineering at my local university. The students I teach are competent coders, who have even got a little bit of group-work under their belts. None of them appreciate the intricacies of environments such as Epitaph because you simply have to be part of it. The things that software engineering courses teach in the abstract are things you are going to learn about first hand. It's important to provide some caveats here. First of all, working well with others doesn't mean you have to like the people you work with. It's always better if you do, but perfectly satisfactory collaboration can occur even when the participants hate each other. Gilbert and Sullivan for example had a notoriously quarrelsome relationship, but it didn't stop them penning some enduring popular works. Actually liking people isn't necessary - successful collaborations can be born from affection, trust, or respect. Ideally you have all three, but one is enough to build a working relationship. The problem comes of course when none of these are present, but those circumstances are thankfully quite rare. It doesn't matter if you like the people you work with, it only matters that you can work effectively with them. We can't force anyone to feel a different way about another person than they actually do, nor would we want to. Part of the skill-set you need to build as a creator is the ability to successfully collaborate. That involves a whole lot of concepts that are new to most people it involves understanding a complex and dynamic social context, as well as 407

Epitaph Online

http://drakkos.co.uk

understanding the software development process. It involves becoming familiar with technologies that are often for rather alien purposes. In short, it's learning a whole lot of entirely new skills. People tend to look down on MUDs as development environments because of the stigma attached. Most MUDs are vanity affairs in which a handful of coders put together an ad-hoc, unprofessional game based on some stock code-base. Such games rarely have more than a dozen or so players, and those players tend to be drawn from already formed social circles. Epitaph is not one of those MUDs - we are a derivative of an extremely large and successful MUD, and many of the developers here (myself included) have moved here from Discworld to pursue our development agenda. Our game is tremendously complex, well structured, and with an emphasis on the programminmg architecture that underlies the game. However we are also a volunteer environment, and that introduces a whole range of new issues. We don't expect people to know how to code. We don't expect people to understand formal software development. We don't expect people to know about the complex etiquette that goes along with multi-developer environments. Over the years, that has led to an adoption of code written to dozens of different standards, in dozens of different styles. That causes many problems. Most of this code we have inherited as part of the DWMUD mudlib from which we derive, and refactoring (see later in this book) is a constant activity of our developers. It's hoped that this material makes you understand the importance of some of the fairly abstract things we tend to insist on here on Epitaph, and why they are not arbitrary exercises in nit-picking. There's a good reason why we ask you to do all of the things we ask you to do.

Standard Standards
This is a rather grand heading for something we don't actually have... Within Epitaph, we all realise that those who come as developers do so as volunteers. That means we have fairly limited leverage in forcing a particular agenda. To be sure, domain leaders have the authority to hire and fire within their own domains, but we much prefer to have people working with us than not. As such, things like our style guidelines are only inconsistently followed. It's my hope that, after reading through this material, you understand why we ask for these things, and that through knowing the intention you'll actually be motivated to follow the standards. It's too late for a lot of people, but if you're just starting out with us it's a fantastic opportunity to get into the habit of writing code that is structurally clean. I would like to add a word of warning here the things that I am going to talk about are surprisingly emotive issues. You will find creators trying to insist that 408

Epitaph Online

http://drakkos.co.uk

their particular formatting style is the best, sometimes 'humorously', sometimes not. This is an extremely unhelpful situation, and I would ask you to ignore any of these comments. It honestly doesn't matter what standard of code that is adopted, the only thing that matters is that everyone uses it. There is virtually no difference in code readability from one standard to another, but it dips dramatically when everyone is using their own standard. We compromise a little so that we all have a more pleasant development experience. I am in no way saying that the style of coding that is outlined in these documents is the best way to layout code. I am making no value judgements at all however, we need to decide on one standard and this is the one we're going to use. We'll talk about that in the next chapter.

Professionalism
Volunteers we may be, but we do like on the whole to maintain at least a veneer of professionalism. Some of us are worse than others at that, but it's an ideal to which everyone should aspire. That means, your personal issues with someone shouldn't get in the way of you fulfilling your obligations as a creator. If you and another person are working on a project, then you have to put aside your disagreements enough to allow a working relationship to emerge. As far as is possible, you should keep personal issues off of the public channels. If you feel you need to tear into someone for their (in your opinion) gross incompetence then do it in tells. Otherwise it's just awkward for everyone. Failure to do this is only going to get you a reputation as someone who doesn't play nicely' with others, and if that persists the only real option is for you to be removed as the obstacle you are. This is an unusual step taken in rare situations, but there are precedents of people who just could not get along with anyone who are no longer creators. It's often harder with some people than it is with others in life, there are just people who rub you up the wrong way no matter what they say or do. Your best bet in such circumstances is to simply try and maintain a degree of civility when interaction is required, and avoid them otherwise. In situations in which you simply cannot resolve your differences, it's worth looking for a mediator ideally someone of higher rank than both so as to allow for binding agreements'. If your problem is with someone in your domain administration, you should arrange for a discussion between all members of the domain administration team to see how the situation can be resolved. In all cases, you want to provide a framework for constructive engagement with your colleagues. Where that can't be achieved, you need to find a way to simply be around them. Nothing sows more disharmony into your fellow creators than a 409

Epitaph Online

http://drakkos.co.uk

persistent and public slanging match. The creator channel, and the boards, are not the place for that kind of thing.

Conclusion
We're going to cover a lot of ground in the chapters of this material, including ground that will be entirely new even for a lot of experienced developers. In all cases, I am going to ask you to engage with the material and not dismiss it as an irrelevance. As I have mentioned above, there is a reason why we ask you to code to a specific style. Although we can't really force it in the same way that can be done when people are being paid for their effort, it makes the MUD a much nicer place for us all to develop if we can rely on a little professional courtesy from our colleagues.

410

Epitaph Online

http://drakkos.co.uk

Code Layout
Introduction
There is nothing that will make your code more readable than having a clean layout. Inversely, there is nothing that will make your code less readable than having a bad layout. There is a set coding standard than we have on Epitaph, and while it has been inconsistently applied over the years it is something you should try to get into the habit of before you are Too Set In Your Ways. This is a surprisingly emotive issue for some people for some reason, people seem to invest a lot of their own self-worth in the choices they make in terms of laying out code. The truth is, it doesn't matter in the least which standard you choose to use they're all equally readable. The only thing that matters is that everyone uses the same standard. Don't be one of those creators who stubbornly refuse to compromise on this point it's stupid, and actively unhelpful. Likewise, don't pay attention to those who try to force their own standard upon you. Just roll your eyes and move on.

Code Formatting
This is going to be a rather dull section, but it's important that we talk about it. I've already said this but I'm going to say it again having a clean style for code is the most important thing you can do to make your code readable. The standard that we apply is as follows: Indent two spaces per level of coding structure. Lines of code no longer than 79 columns. The opening brace of a structure is placed on the same line as the structure to which it belongs. All defines should be in UPPER CASE. Functions are all in lower case, with an underscore separating words. In java, a function might be thisIsAFunction. In LPC, that would be this_is_a_function. All for loops and if statement to have opening and closing braces, even if they are not syntactically required. Use spaces, never tabs!

That's all it's not much to remember, and once you get into the habit of it you'll do it subconsciously. When I started coding on Epitaph, my own personal standard was contrary to all of these. As time went by, I migrated towards the Epitaph standard because it made things much easier for everyone involved, and it came as no real cost to me. Let's look at two bits of code, one without formatting, and the other formatted to Epitaph standards. First, without formatting: 411

Epitaph Online

http://drakkos.co.uk

void this_is_a_function() { for (int i = 0; i < 100; i++) {if (i % 2 == 0) {tell_object(this_player(), i + " is an even number.\n");}else {tell_object(this_player(), i + " is an odd number.\n");}}}

And then formatted to our internal standards:


void this_is_a_function() { for (int i = 0; i < 100; i++) { if (i % 2 == 0) { tell_object(this_player(), i + " is an even number.\n"); } else { tell_object(this_player(), i + " is an odd number.\n"); } } }

Hopefully the latter example is obviously more readable. There are also coding clues given for you indenting to a different level depending on the depth of the structure gives you an instant visual hint as to where opening and closing braces should go. It demonstrates ownership you know that the if statement belongs to the for loop, because that's what the indentation shows. Look at those two samples again, slightly altered:
void this_is_a_function() { for (int i = 0; i < 100; i++) { 0) { tell_object(this_player(), i + " is an even number.\n"); tell_object(this_player(), i + " is an odd number.\n"); } } if (i % 2 == } else {

This code won't work, and it's not immediately apparent why. On the other hand, if we reformat it:
void this_is_a_function() { for (int i = 0; i < 100; i++) { if (i % 2 == 0) { tell_object(this_player(), i + " is an even number.\n"); } else { tell_object(this_player(), i + " is an odd number.\n"); } }

The eye is instantly drawn in the second example to the fact a closing brace doesn't exist where we would expect it to. The layout actually makes it easier to code. This additional readability can be lost when multiple people with different coding styles work together on the same file:
void this_is_a_function() { for (int i = 0; i < 100; i++) { if (i % 2 == 0) { tell_object(this_player(), i + " is an even number.\n"); } else

412

Epitaph Online

http://drakkos.co.uk

tell_object(this_player(), i + " is an odd number.\n"); tell_object (this_player(), "This is a line that appears on " "every number!\n"); } }

Rather than this code being easy to use because standards have been applied, it becomes harder to read because inconsistent standards have been applied. Now that we've seen the difference the formatting makes, we'll talk about each of these rules in turn and why they are in place. Indent Two Spaces Per Level of Coding Structure There's no magic formula as to why two spaces is best, other than it gives you slightly more screen real-estate to work with while still showing the relationship between coding structures. I will emphasise something I said before people will try to convince you that the number of spaces they use is a better way of laying out code than the number of spaces this document tells you to use. Pay these people no heed, for they are deeply tedious. There is no real difference between two spaces, three spaces, or four spaces. The only thing that matters is that all agree to use the same level of indentation. Lines Of Code No Longer Than 79 Columns This is a readability issue many people are still working within the 79 columns of a standard telnet display. If you have code that goes over that line length, it is virtually unreadable. For example:
add_conditional_clone_item ("christmas tree", "There is a bit of a bustle, an d $N is " "erected by happy little elves.", (: SPECIAL_EVENT_HANDLER->query_current_special_event ("christmas") :), (: $2->add_property ("there", "in the corner of the room") :), "There is a bit of a bustle, and $N is packed away by subdued little elves. ");

Sadly, that's from a piece of my own code... It makes even room descriptions hard to read, so imagine what it does for complicated code structures. The Opening Brace of a Structure Once again, there's no reason why this should magically be so it's just that in order for there to be a standard, everyone has to do the same thing. As with the two space rule, it provides a little extra real estate on the screen when viewing 413

Epitaph Online

http://drakkos.co.uk

things in the very restricted environment of the MUD. All Defines Are In Upper-Case Having an at a glance' way to tell which values have been defined and which are drawn elsewhere from the code aids tremendously in readability. Moreover, when defines aren't in all upper case, it dramatically detracts from readability because everyone expects the alternative. Violating that assumption has a measurable impact on code comprehension. Functions Are In Lower Case This is usually a convention of the language rather than a convention of Epitaph particularly. The stylistic conventions obeyed as part of the internals of the programming language define how our functions are to look. The MUD's efuns for example use this_kind_of_standard and so that's what we use for our own code. Otherwise we need to make a mental check each time we use a function is this an efun, an sfun, or an lfun?' and choose the formatting accordingly. All Structures To Have Opening and Closing Braces LPC allows you to omit these on for, while and if structures if you have a single line of code to be executed:
for (int i = 0; i < 100; i++) tell_object (this_player(), "The number is " + i);

This is syntactically correct, and it will work as intended. However, when you come back to add more complex functionality you need to remember to put the braces in, and those who work with your code need to be observant enough to notice that you haven't already put them in place. You gain nothing from omitting them it's like using an indicator in a car, you should use it even if you're alone on the road because it's easier overall when such an activity is an unconscious rather than conscious decision. This is an example of an area in which competing formatting standards will actually cause a decrease in readability. Imagine person one, who indents to two spaces and doesn't use opening and closing braces. then person two, who indents to four spaces:
for (int i = 0; i < 100; i++) tell_object (this_player(), "The number is " + i); tell_object (this_player(), "Some stuff\n");

The visual clue here suggests that these two statements are part of the same for loop. In fact, only the first belongs. This kind of ambiguity can be reduced by simple layout and clarity of expression:
for (int i = 0; i < 100; i++) { tell_object (this_player(), "The number is " + i);

414

Epitaph Online } tell_object (this_player(), "Some stuff\n");

http://drakkos.co.uk

Again, it costs you nothing to put in the braces, so you should get into the habit of it being a muscle memory' thing rather than a conscious choice. No tabs If you put a tab in your code, it creates a very ugly visual artifact when you read it on the MUD it gets interpreted as <TAB>:
bookcase->set_long( "This bookcase is made from oak and " "varnished to bring out the glow. It has 2 shelves, " <TAB>"upon which you can see some books, and other objects.\n" );

Bleuch. That's horrible! Instead, use spaces, never use tabs. Luckily, if you are using a good text editor you can get the best of both worlds you can tab as much as you like, and the editor will simply interpret it as a set number of spaces. Make It Easy On Yourself Provided you have a good text editor, a lot of this can be handled for you. We'll use Ultraedit as our example of this. Other editors will undoubtedly have similar facilities, but these are left for you to discover. First of all, we want to remove the tabs from our code. This is the most important first step to take. Go to Advanced->Configuration, and that will open up the configurations editor. Navigate to Word Wrap/Tab Settings':

415

Epitaph Online

http://drakkos.co.uk

Three of our rules can be automated for you make sure use spaces in place of tabs' is selected. Notice here that we can set the Tab Stop value and the Indent Spaces value set both of these to two. Finally, you can also automate adherence to line lengths by setting the wrap method. Ultraedit will thus do a big chunk of the work for you, without you needing to worry about it all.

Conclusion
Standards are a good thing, and it would be tremendously helpful if you could get into the habit of writing your code to them. Over the years we have had an inconsistent approach to formatting, varying across domains, individuals and even time periods. While it may have been more convenient for single people, we are a team trying to achieve a collaborative goal. Everyone has to be willing to compromise on this to make the whole project work together better. In the case of code layout, you're not even being asked to compromise much no matter how fond you are of your own particular style, it is not so much better than any other style that it justifies the lack of clarity that comes from inconsistency. Additionally, please ignore those people who try to force you to deviate from these standards with mockery, or bizarrely strident advocacy. There is nothing Big and Clever about trying to undermine any effort to increase consistency of code across a massive developer-base. In real world coding environments, you code to the set standard or you lose your job that's not an option we like to consider here on Epitaph the best we can do is appeal to your presumed desire to be a useful, valuable member of a highly integrated and collaborative team.

416

Epitaph Online

http://drakkos.co.uk

Collaboration
Introduction
We are big on collaboration on Epitaph. At least in theory. In actuality, everyone has their own particular approach to how and when they collaborate. However, game development is an inherently collaborative endeavour, as the things that you make available in the game will have an impact on many other things. If you make a bank available, it alters the flow of money for all domains. If you add a vault, it affects game performance. The connections are incredibly complex. We have several good tools in place for enhancing collaboration, but they are for naught if the will to collaborate is not present. In this chapter we'll talk a bit about the collaboration styles you will tend to encounter, both here and in 'real life' environments.

The Social Context of Collaboration


The social context of an environment is one of the key elements in fostering an atmosphere that supports collaboration. Every context has its own particular features. Epitaph has a strong tradition of meritocracy in advancement, and this meritocracy is usually demonstrated through a system of emergent authorial leadership. In essence, you progress by showing yourself to be a 'safe pair of hands' on the basis of the projects you are involved with and the contribution you make. In addition to this is the value ascribed to seniority combined with the authorial leadership, authority accrues to those who have been around sufficiently long to be considered 'tribal elders'. This pattern is also reflected in the playerbase, where a distinction is made between 'newbies', 'midbies' and 'oldbies'. The combination of these two social dynamics is common to many collaborative, volunteer endeavours. Collaboration is enhanced by the further tradition of 'ownerless code'. Code on Epitaph does not belong to any particular creator, although one creator may take a greater or lesser interest in its upkeep and maintenance. Code instead belongs to a domain in the first instance, and the MUD as a whole in the second. It is not only possible for another creator to modify code you have written, it is actually an active part of the development context. Everyone owns the code in their domain, and there are creators who have wider responsibilities that work across domains. It's important that you understand your code is Communal Property, otherwise you'll find it very difficult to cope especially if your code is important enough for people to take an interest in. A sense of shared responsibility over code, and a tradition of authorial leadership, are important traits in a successful collaborative environment. These features are apparent in one of the most successful of modern collaborations Wikipedia. 417

Epitaph Online

http://drakkos.co.uk

However, the social context is modified by the people who are involved, and some people simply do not collaborate. Our environment facilitates collaboration but doesn't mandate it you don't need to take anyone's views on board while doing your development with the exception of those of your domain administration. Some people work best like this, but it's not a mindset which we like to encourage. There is often a generational gap that comes along with willingness to collaborate. Older developers may be less willing to engage in such a process, because they perceive development as a solitary effort. This is not something that is especially pronounced in Epitaph, but it can be observed in other environments. Younger developers now grow up in an atmosphere of extreme collaboration, brought on by a culture of social networking and the prevalence of shared wiki tools. Older developers are less familiar with this as a mindset, and so are often somewhat resistant to broad and indiscriminate collaboration, preferring to collaborate instead with a few hand-selected and trusted colleagues. Different people have different ideas about what collaboration actually means. Does it mean the intense collaboration of something like Wikipedia where changes are small but accumulate with the weight of an avalanche? Or does it mean that one person writes something, and another person writes a bit, and the first person writes a bit more essentially serial development. Or does it mean that both individuals make their own attempt, and then the best of these two attempts are merged together? All of these describe different, but perfectly acceptable, models of collaboration. You also tend to encounter one or two people who actively disapprove of collaboration. It's not just that they don't collaborate themselves, but they actively despair of collaboration in general. Authors such as Jaron Lanier have written of an encroaching 'digital Maoism' in which individual ability is swamped by the mediocrity of averages. When building a social context, it is important to start with the people. For collaboration to occur, first and foremost there must be a will to collaborate. Some environments are set up in such a way that collaboration quite simply will not happen. In a now-famous paper, Wanda Orlikowski discusses an attempt to introduce a groupware product to a team of consultants; the implementation failed, due to endemic social issues stemming from competition and no tradition of mutual trust, as well as a deep lack of communication as to what the tool was for. The mindset many developers adopt when introducing collaboration technology is what I like to refer to as the Field Of Dreams mindset 'if we build it, they will come'. Experimental evidence however shows that this is hardly ever true people have to see a need before they make use of the tool. Collaboration tools work only if they make existing social processes easier to mediate.

Development in Volunteer Environments


418

Epitaph Online

http://drakkos.co.uk

Why do people choose to give their time and effort, for free, to a cause like Epitaph? Everyone is going to have their own reasons for this, but there are certain commonalities within different projects. Many people report 'altruism' as a reason for participation. Whether altruism actually exists or not is a philosophical quandary, but what can't be denied is that people often feel a pull towards a cause in which they believe. Presumably you enjoyed your time playing Epitaph, and felt sufficient draw to the game that you wanted to devote your time to making it better. This is something reported often in open source communities. However, there are many additional benefits that come from participating in a project like this. For one, you develop many skills that are genuinely marketable. A number of Epitaph creators have profitably included their development experience on their CVs when applying for jobs, and attribute at least some measure of their success in those interviews to the skills they have developed here. Many professional developers despair of the lack of attention paid in university educations to 'operational skills' such as dealing with source control Epitaph gives developers exposure to the complexities of working within a codebase of quite staggering complexity and size. That's something that sets you apart right away from many other developers. There is an interesting element to how people choose to join as creators in the first place unlike most volunteer coding movements, Epitaph does not incorporate simply any change from any interested developer. Instead, Epitaph is a 'hybrid open source' environment in which the game files are secret, the driver is freely available, and public releases are made of the core mudlib. Self-selection of contributions is a big feature of environments such as Linux or Apache, but it is not reflected in our approach to development. Developers self-select in so far as they choose to apply, but the process is much more like applying for a job than it is developing for Linux as a movement. This has issues of scale that often manifest themselves when a domain leader is absent, a domain can grind to a halt. The administration of a domain is responsible for the ultimate vetting of quality. The playtesters domain is an opt-in service for those domain leaders who wish to make use of it, but the exact system for determining whether or not a development is to go into the game varies from domain to domain. Some domains make extensive use of peer review (as Forn did during its development of Genua), while others have more informal processes in place.

What Are The Benefits of Collaboration?


There are several benefits that come from collaboration. Linus Torvalds, the man responsible for building the first version of the Linux kernel, is credited by Eric Raymond with formulating Linus' Law. This states, informally, that 'given enough eyeballs, all bugs are shallow'. Collaboration allows us to harness the different skills and abilities of many people we end up with the whole being more than the sum of its parts. 419

Epitaph Online

http://drakkos.co.uk

It's often surprising the wealth and depth of experience that is available when you widen the parameters of your search. In any room of average people, you'll find individuals with the strangest combination of skills some because of their occupation, and some because of their hobbies. The combination of skills that people develop over the course of their lives lends a unique perspective to their views and opinions everyone views the world through the lens of their own experience. Aggregating the views of people with multiple sets of skills and abilities can lead to results that are better than any single individual is capable of producing. The book 'Wisdom of the Crowds' by James Surioweicki is an extremely interesting discussion of this, and it is very relevant to the idea of intense collaboration in programming environments.

Collaboration Tools on Epitaph


We have numerous tools for persistent communication and collaboration within Epitaph. At the simplest level is the internal mudmail and board system these allow for communication, and at the core that's what collaboration is all about. However we also have two tools that fall into the more modern category of 'collaboration software'. The first of these is our extensive wiki system we use the mediawiki engine for this. Everyone shares a wiki on Epitaph, so you can get a good cross-domain and project overview of what's going on in the game. There is a point of critical mass that needs to be reached for such tools to get momentum. Simply using a tool, even if no-one else is using it, can generate interest, and that can in turn generate further contributions. All it takes is one person to get the necessary traction. Encourage people to read your contributions direct them to your wiki page when you're asked questions. Get people to look, and you might just get people to join in. The Wiki may be found at http://survivor.imaginary-realities.com/wiki/index.php?title=Main_Page. Have a read through - you may be surprised at what you find. The second tool we have is our bespoke knowledge management software the Epitaph Oracle. This is a system for collaboratively eliciting the considerable knowledge and expertise of our creator base. I would encourage anyone who has a question, no matter what the question may be, to check Oracle to see if the information is available, and then ask the question if it is not. Everyone who asks a question is engaged in the task of knowledge elicitation you are helping to make information available for all those who follow. The Epitaph Oracle may be found at http://drakkos.co.uk/secure/oracle/oracle.c?base=creator. Please contribute anything you think might be of interest, and ask any questions that come to mind. 420

Epitaph Online

http://drakkos.co.uk

A Suggested Collaboration Process


First of all, your task is gathering ideas. You'll undoubtedly have many of these yourself, so create a wiki page for your project and outline them. You'll find there are people in the creatorbase who read every change made to the wiki (I'm one of them, I'm a wikiholic) so even if no feedback is received it doesn't mean your contribution hasn't been read. Update the page as thoughts occur to you it can be a useful project planning document for you and for your domain leader. For a domain leader, keeping track of where each project is and how complete it is is a complex task. The best thing that you as a developer can do to ease this task is to keep your own developer page up to date - that can be tremendously helpful. If you are developing a specific area, you might want to consider making an abstract of the area available on your domain wiki, outlining features and quests. Small areas may not lend themselves well to this, but it's great when you can make use of it. Once you've got your wiki page up and running, try making a post to your domain board outlining what your plan is and where the wiki page can be found. A simple request for 'any thoughts people may have' can elicit many useful suggestions (though sometimes, no suggestions at all). Occasionally you will find that a project has interest to someone beyond the people you would normally expect, and that can lead to profitable, albeit unexpected, collaboration. As an example of this, if you were coding an island full of pirates, it would certainly be of interest to me as part of the development of the piracy system. If anyone expresses a particular interest, have a chat with them to see the level of their interest. You may find people who are interested in supporting particular parts of the development, or who are interested in hooking in code of their own. All of this is a great opportunity to make your area richer than it would be when developed from the perspective of an individual. Check with your domain administration to see if you can canvass the playtesters for suggestions. The ptforum board is a great place to see what ideas they may have for things they would like to see, or things that they definitely wouldn't like to see. The more perspectives you can solicit, the more of a pool of good ideas you'll have to choose from. It's important to note here that this doesn't mean you abdicate ownership of your project you are soliciting feedback, but that doesn't mean you're obligated to use it all. It's just that getting a wider range of perspectives will give you a much better foundation from which to develop your thinking.

421

Epitaph Online

http://drakkos.co.uk

Conclusion
Collaboration is an entirely social problem you should not confuse the tools with the concept. Our tools exist to support existing social dynamics, not supplant them. Collaboration is an important part of what we do on Epitaph everything impacts on everything else in very complex and complicated ways. Our unique cultural makeup has led to the emergence of certain organisational norms a shared ownership of code, authorial leadership based on meritorious contribution, and a general respect for those who have contributed long enough to have become 'tribal elders'. All of these help support an environment in which collaboration can flourish, but it still requires critical mass for it to be effective. You can either be a barrier to collaboration by looking inwards, or you can be a spark for further collaboration by doing your best to solicit feedback.

422

Epitaph Online

http://drakkos.co.uk

Social Capital
Introduction
Social Capital is the glue that keeps a society together. As a term, it refers to the reserves of trust, respect, collegiality and norms of reciprocity that exist in social networks. It's a measure gaining considerable traction in sociological and economic debate while it can't be quantified, it provides valuable qualitative analysis of the level of function and dysfunction in an organisational environment. What we're going to talk about in this chapter is how social capital is built in a online technical context like Epitaph. The creator-base has a rather extreme reputation for 'creator politics', but in the main this stems from a handful of isolated but extreme problems, rather than being a systemic feature of the environment.

Creator Politics and MUDs


We're a new MUD, and thus we haven't had enough time to blot our copybook as far as all the drama and upsets go. As such, these comments will centre on my experience at a certain other MUD. The fact is that creator politics in most successful MUDs are nowhere near as endemic as the popular player perceptions would indicate. Player perceptions are distorted by the handful of disproportionately loud examples of ex-creators who were either fired, or resigned, because of their inability to integrate into our working environment. Those who complain the loudest about not being promoted because of 'politics' are those who, invariably, have not played their part in engaging in the collaborative process of building a lasting reserve of social capital. That's not to say that there are no politics as soon as you put more than one person in a room, politics suddenly happen. Politics is the word we give to the necessary friction and abrasion that comes from people having multiple, perfectly valid, viewpoints. Many outlandish claims are made about creator politics. There are claims of institutionalised nepotism, lasting grudges, and projects that have been killed because the wrong person was involved with them. While there will be examples of each and every one of these, they are not widespread they stick out precisely because they are not widespread. Follow the gingerbread trail of rumours to their sources, and you'll find that a small number of bitter ex-creators are responsible for their perpetration. These creators, without exception, found it difficult to work as a creator because of their own unwillingness to engage fully in the process. So, put these complaints in perspective. The politics amongst Epitaph creators are no more cut-throat than the politics you will encounter amongst any group of people. If you are mature enough to try and work with other people, you'll find 423

Epitaph Online

http://drakkos.co.uk

they are willing to try and work with you.

The Ten Commandments Of Egoless Programming


Way back in 1971, a guy called Jerry Weinberg wrote a tremendously influential book the Psychology of Computer Programming. In this book, he outlined a mindset he termed 'egoless programming' as a way to deal with the often emotive issue of ensuring quality in software development projects. His system revolved around the ten commandments that tend to lead to a more positive, collegial relationship between software developers. As a 'consciousness-raising' exercise I would like to outline them here because they serve as a useful set of precepts for how trust and respect flow in development. The commandments are his, although the commentary is mine. Understand and Accept You Will Make Mistake We all make mistakes some of us more than others. The consequences of these mistakes may be minor, or they may be a major inconvenience to the entire userbase of the MUD. I once made a particularly bone-headed error that locked the MUD up tight for a good hour, something that would have been impossible to do if I hadn't explicitly switched off the sanity checking built into the driver. You will make mistakes learn from them, and move on. You Are Not Your Code When people criticise your code, they are not criticising you at least, they shouldn't be criticising you. If they are, then it's a problem with them personally. Constructive criticism is very valuable it's how you learn from people with a little more experience. In order to accept constructive criticism, though, you need to divorce yourself from the code you have written you need to be able to take a dispassionate view and say 'Ah, yes it does indeed have defects I need to address'. No Matter How Much "Karate" You Know, Someone Else Will Always Know More. It doesn't matter how good you are there's always someone better. There's one person in the world for whom that isn't true, and that person is perpetually looking over their shoulder for the day that it is. Moreover, everyone has their own particular areas of expertise even if you consider yourself the Top Guru in a particular area, someone else is going to know more in another. Creating on Epitaph requires a very odd blend of skills, and some people have these skills to greater or lesser extents. Even if you know you're good, don't let it go to your head the chances are you're not as good as you think you are Don't Rewrite Code Without Consultation We don't encourage creators to 'own' code on Epitaph code is a communal 424

Epitaph Online

http://drakkos.co.uk

resource. However, that doesn't mean you can write code without regard for other people. This is especially true when you are working with lower level inherits and critical handlers there is an etiquette that goes with rewriting code, and it is vital you adhere to it. If you are going to do some serious remodelling of important code, then make sure you consult with the people who are likely to be affected. Treat People Who Know Less Than You with Respect, Deference, And Patience I have my doubts about the choice of wording with regards to 'deference', but the general rule is worth keeping in mind. Everyone was a beginner at one point, and if those to whom we had turned for help had mocked and dismissed our queries, the chances are none of us would be here at all. It's an act of considerable courage to ask for help, and it's in everyone's best interests for every creator on Epitaph to be as good as they can be. Encouraging and constructively engaging with creators who have queries is the best way to foster an atmosphere in which self-evaluation and improvement is possible. Additionally, the very act of asking a question can add value to the creator-base. A good question will tax the understanding of the teacher as well as the student; the teacher gains a little extra clarity, and the student gains the understanding they desire. If questions are asked through the Oracle system, then a good question is worth its weight in gold to the creators who come after you. The Only Constant In the World Is Change On Epitaph, we are not beholden to the whims of our clients. Our clients are the players of the game, and they play the game which we provide. In other organisations this is not the case when a client asks you to make a change, you make it. However, even though we are the ones setting the development agenda, we all still need to learn to deal with changes in fundamental coding tools and systems. Changes elsewhere in the game will impact on the code you are writing, and you must be willing and able to adapt your code to deal with emerging situations.

The Only True Authority Stems From Knowledge, Not From Position Those who have attained higher rank in the creatorbase have usually done so on the basis of their contributions to the game. While you should accord people the appropriate level of respect, you shouldn't confuse position with authority. The newest creator may have more knowledge of a particular area of the game than the most senior creator, and you shouldn't allow the position of the latter to override the expertise of the former. Fight For What You Believe In, But Accept Defeat Gracefully Everyone has a different view on what's important in the game, and everyone has 425

Epitaph Online

http://drakkos.co.uk

a different view on what will improve the game. It is important that you fight for what you feel to be right, but you also have to realise that the authority for making the ultimate decision usually does not reside with you. In such circumstances, it's important to let go of the debate and accept the conclusion, even if you personally disagree with it. We've all had to live with the consequences of decisions that we didn't like. Don't Be the "Guy In The Room" The "Guy In The Room" is the one who doesn't engage with the rest of his or her team. The Guy has a project, and writes the code for that project without collaboration or input from others. The "Guy In The Room" doesn't know what's going on and isn't really a part of the team. Not knowing the broader context in which code is being developed and deployed is a major disadvantage in an environment like Epitaph, and you'll end up doing yourself more harm than good. Critique Code Instead Of People Be Kind To The Coder, Not To The Code Too many people don't understand what 'constructive' means. Additionally, too many people deliver criticism without even a basic understanding of human psychology there is a reason why such people very rarely manage to convince others of the worth of their remarks. Critiques should focus on the code, and not the coder. 'You write code that is full of problems' is an attack on a person, and that's never going to get someone on side. 'There are some problems that need to be resolved with this code' focuses the remarks where they belong. Of course, if Commandment One is not being observed, it's not going to make a lot of difference. The number of people who don't understand the most productive way to deliver criticism is staggering. In the main, it's how you do it rather than what you say. To begin with, concentrate on everything that is right about the artifact in question start with the positive, and then introduce a discussion of the negatives. If necessary, interleave positive and negative feedback to ensure you're not simply giving a laundry list of flaws. The important thing about positive feedback is to front-load it it's counterproductive to start off with negative feedback since it just puts people on the defensive, and even if there are positive comments at the end, you've already lost the chance to win someone around to your way of thinking. Inability to deliver criticism correctly is the leading reason why some people just can't get others to listen to their feedback.

Trust and Common Ground


Perhaps one of the trickiest aspects of this system is that it requires you to be able to trust and respect your colleagues. Trust is the alchemical property that makes sure teams keep functioning even when there are breakdowns in communication, conflicts of interest, or simple personality clashes. 426

Epitaph Online

http://drakkos.co.uk

Invariably there will be people you trust more than others, and with whom you feel these commandments can usefully apply. Conversely, there will be people you don't trust, either in terms of their personality or their competence the thought of following these commandments with these people would be laughable. However, once trust is built it can work effectively to bridge the day to day problems that collaborative development will introduce. There are several ways in which trust can be built in any organisational environment. They are based on a genuine willingness of all participants to work towards a common understanding. First of all, trust is built on common ground this is the set of principles and issues on which you and your interlocutor can agree. This involves a certain amount of give and take, and a willingness for each to see things from the other's perspective. Unwillingness to build common ground on the part of one participant is a sign that they are either not interested in or not capable of participating in a constructive dialogue. Common ground tends to increase on the basis of familiarity with individuals, and familiarity with a particular environment. It is particularly strengthened when it involves shared experiences or a shared background. Common ground requires a willingness for individuals to compromise in organisations, it's based on the willingness of an individual to be integrated into a complex community, rather than being an outsider unwilling to consider more productive engagement. Most organisational culture is a manifestation of common ground within a particular operational context the 'work songs' of IBM in the fifties were comical, but they served their purpose in creating (an arguably somewhat dangerous level of) common ground. Common ground can be maintained by simply 'keeping people in the loop'. That doesn't mean that everyone has to be updated about every little detail of your project, but it helps if you touch base with the right people at the right time. This helps resolve ambiguities about what people are doing, and prevents small problems becoming larger conflicts. Conversely, conflict causes people to withdraw from the process of building common ground, creating a self-reinforcing cycle of disharmony. It may sound trivial, but common ground requires constant calibration. It's not something that is achieved and then you move on, it's something you continually work towards. Even when common ground disappears, trust is often enough to ensure the resilience of a team of people over the short term. Where there is no trust, our assumptions of motivation are always flavoured negatively we assume people are doing things for the worst reasons, rather than for the best. When there is no trust, there is no will to collaborate, and this strikes at the heart of the way our environment works. When there is no trust, mistakes are hidden rather than brought out for everyone to help resolve. When there is no trust, people are less willing to say 'I don't know', and that is no good for anyone. 427

Epitaph Online

http://drakkos.co.uk

So, if we know how common ground is built, how do we build trust? Sadly, this can be much harder to do in an online rather than an offline environment. Trust is built as a consequence of informal social interaction coffee breaks, having lunch together, and idle chit chat. We do have channels for discussion, but our environment poses numerous extra challenges. For one thing, there is an inherent ambiguity in the medium. If you ask me a question, and I don't respond, why is that? Is it because I'm not actually at my keyboard? Am I actively ignoring you? Did it just slip past as I blinked during a wave of debug spam? It's hard to tell. Likewise, there is none of the nuance in written text that comes with face to face communication. The words themselves are only a part of what we project when we talk to someone tone of voice, facial expression, and body language are all vital in decoding the actual intent of the words. We have smilies in text, but they do not do nearly enough to bridge the gap. Where trust already exists, one can read the best intentions into communication. Where trust doesn't exist, we can read the worst. One of the easiest ways to completely demolish any initiative to build trust is to talk behind someone's back rather than bring it up directly people almost always hear the backroom gossip anyway, and when they do it lowers you in their perception. My own personal rule for this is 'Never say in private what you are unwilling to say in public'. While that may get me a reputation for being 'A Bit Of A Dick', at least people are sure that if I have a problem with them, I will bring it up directly rather than passive-aggressively. Additionally, the simple nature of our distributed developer base is problematic it's often not possible to get immediate feedback on such communication because it was written asynchronously we were sleeping when it was written, and now the person who wrote it is asleep while we read. Keeping the channels of communication open are the best way to build trust just talk to people. It doesn't have to be about the game, although if you have a project that's exciting you can go a long way by communicating that excitement. In our particular environment, individual initiative is hugely important, because it's what allows you to work when there is no instantly available authority while you need to touch base with your domain administration, you also don't need to wait for them to approve every little detail. Of course, this is a two-way street you don't build trust if you're the only person doing it. Everyone needs to be willing to work towards it.

The Trust Triad


In the end, trust boils down to three key elements. If you trust someone, you have to: Have the capacity for trust. Some people are so damaged by their life 428

Epitaph Online

http://drakkos.co.uk

experiences that they simply find it impossible to let themselves trust others. Have confidence in their competence. The person with whom you are building a trust relationship has to have demonstrated their capability within their role. Have confidence in their intentions. Where ambiguity rules, it's your confidence in someone's intentions that will carry you through. If you trust someone, you have to believe that they are actually doing what is best for the game rather than what is best for themselves.

As to the capacity for trust, that's an internal measure. The only one who can build that is you, and deeply seated trust issues are well outside the scope of this material. The other two are professional measures, and they are driven partly by you, and partly by the other person. There's a great old saying, 'Never attribute to malice what can adequately be explained by incompetence' it's an aphorism of dysfunctionality in team work. In a team that is functioning properly, you shouldn't have to rely on either extreme. You'll simply accept the fact that 'people make mistakes' (Commandment #1), and work to fix it. Another word for this kind of institutional trust is 'respect'. Notice nowhere does it say you have to like someone; you just need to have sufficient respect in them and their abilities that you can assume the best. You can like people and not respect them, and you can respect people but not like them. The best state, of course, is when you both like and respect them. If you can only manage one though, try for respect. I have often made the statement that 'respect is earned, not given', which invariably causes disagreement along the lines of 'you should start off respecting people'. This disagreement, I feel, is due to a lack of common ground as to our perception of what respect actually means. The absence of respect is not disrespect. You should, by all means, be civil even friendly to people who you have no cause to respect. Active respect, though, demands a little more from both parties. It requires adherence to the 'trust triad' outlined above and this can't be done with someone you've only just met. They need to have proven themselves, and shown that their intentions can be trusted. That takes time, and a willingness to actually build that trust, and it needs both parties to engage in that process.

Conclusion
For all you hear about 'creator politics' in closed social contextx, the fact is people do try, in the main, to get along. There are always politics that's what happens in life. You put people together, and politics is one of the by-products of whatever activity was the intention. There are examples of systemic distrust between individuals, and examples of complete breakdowns in communication. If you look a little deeper though, you can often find that the reason for such situations is that one or more of the participants have simply withdrawn from the exercise of 429

Epitaph Online

http://drakkos.co.uk

building trust and maintaining common ground. If you keep this as an active priority in mind when developing, you'll find it easier to function in our somewhat overwhelming world. People who get on well with people have a 'superpower' all of their own they keep the MUD running when communications have broken down between others.

430

Epitaph Online

http://drakkos.co.uk

The Dark Art of Refactoring


Introduction
A lot of what we do as creators is tidying up code that has already been written. Technically this is known as 'refactoring' code. It has something of a reputation as a 'dark art' amongst programmers, mainly because so few genuinely understand what the process of refactoring involves. It's actually a very simple principle, albeit with technical complications that go with it. In this chapter we're going to talk about refactoring how it's done, why it's done, and most importantly of all, how it shouldn't be done.

Refactoring
Put in its simplest, most accessible terms refactoring is the process of turning bad code into good code, while not impacting on any of that code's functionality. Refactoring is ideally an invisible process if you do it right, no-one should know you did anything at all. Refactoring is not about adding extra functionality, although it may be a precursor to this. It's also not about fixing bugs, although bugs may disappear as a consequence. Many of the oddest random bugs are a result of badly structured code, and cleaning up the internal architecture of a problematic object can result in real, observable improvements even though that was not the actual intention.

Good Code
Part of the problem people have with refactoring as a process is that it is inherently subjective. It's usually pretty easy to identify bad code, but it's much more difficult to identify good code. Different coders will vary in their opinions as to what exactly a good piece of code looks like, and this subjectivity is at the heart of what makes refactoring somewhat non-intuitive. As you grow more experienced as a developer, it becomes easier to identify code that you personally class as good - that judgement is built on the basis of experience. 'Ah, yes I've worked with code like that before, and it was easy to make my changes'. Since refactoring is the process of turning bad code into good code, we need to have a pretty solid grasp of how the code should be improved. You need to be sure your change won't introduce new problems there's no point after all rewriting bad code so it becomes different bad code. We want the quality of the code to actually increase from our efforts.

Impact of Change
431

Epitaph Online

http://drakkos.co.uk

Before we get to the discussion refactoring properly, let's talk about a concept that doesn't get enough discussion on Epitaph the impact of change. We touched on this very briefly in Intermediate LPC when we talked about visibility modifiers for methods and variables. In its simplest term, this relates to the number of objects that will need to be altered if you change core functionality in another object. Epitaph has, in the main, two kind of objects that carry with them a potentially high impact of change. The first are handlers, such as the armoury and the taskmapper. The impact of change that goes with each will vary with how widely they are used. To get a clear picture in your mind, think 'What would happen if I broke this object right now?'. If it's something like the special events handler, it may go unnoticed for a short while. If it's the taskmaster, everyone will be complaining in seconds. This gives you a rough measure of the impact of change. On the other hand, if you break a single room in an area, then that one room becomes inaccessible. That's not a huge deal. Break the inherit that every room in that area uses, then the whole area becomes inaccessible. That's a bigger deal. Break STD_ROOM and the entire game world becomes inaccessible. It is this that defines the impact of change. On the whole, you can get by with four categories of object: Object Impact Critical High Medium Low Examples Core handlers, the lowest level of inherits (the ones that every single object inherits). Other mud-wide handlers, and the rest of the inherits in /mudlib/inherits/. Local area handlers, area level inherits Single rooms, single NPCs, single items

That's one measure of impact of change. The other measure is how many objects make use of the functionality of another object. There is usually a fairly close overlap between categories. There are rules that go along with refactoring, and one of these rules is to be very careful when dealing with objects with high impact of change. Object orientation as a programming framework also has a number of tricks for making objects as amenable to change as possible. We'll talk about them in this chapter also.

The Rules
Here are the rules that go with refactoring. Note, these are rules, not guidelines. A good software development process will obey these rules and have punishments for transgressing them. Methods and variables may be made more visible. They may not be made less visible. The functionality of public methods cannot change. If a public method does 432

Epitaph Online

http://drakkos.co.uk

X, it should continue to do X (and nothing more or less) after it has been refactored. The return type of a method cannot change unless that change is for it to be less restrictive (from a string to a mixed, for example) The name of a method or public/protected variable cannot change. The parameter list of a method must remain the same, or there must be a translation scheme in place for a change.

These are restrictive conditions, and necessarily so. In a massive code-base like Epitaph, you have to assume that if something is accessible to other objects, then some other objects have made use of it. People will have looked at the object and said 'Wow, the do_groovy_stuff method does exactly what I need' and then made use of that method in an entirely unrelated piece of code. By default, all methods and variables in an LPC object are publicly accessible. That means every object in the game has access to the methods, and also to the variables (although variables are at least a little protected by the comparatively primitive object model of LPC). Protected methods and variables are available only to the object in which they are defined, as well as all subclasses (all classes that, somewhere along the way, inherit the base class). Private methods and variables are accessible only to the object in which they are defined no external objects, and no subclasses. These are known as visibility modifiers.These also map onto categories for impact of change: Visibility Modifier public protected private Impact of Change High Medium Low

Thus, an example of a forbidden refactoring would be to turn a public method into a private method. This breaks our first rule it reduces the visibility of the method. Any object making use of that method will break as a result of our refactoring. We could take a private method and make it public this increases the visibility, but is almost never a good idea. There's usually a reason why a method has been given restricted access rights. This isn't the place for a discussion of proper object oriented design though. You can't change the return type of a publicly-accessible method without violating the rules. This, for example, would be a forbidden refactoring:
int add_nums (int num1, int num2) { return num1 + num2; }

Into:
double add_nums (int num1, int num2) { return to_float (num1 + num2); }

433

Epitaph Online

http://drakkos.co.uk

Likewise, you can't change the type, order, number or meaning of parameters in a method unless you provide some way for that change to be transparent to all the objects making use of it. This would be invalid:
int add_nums (int num1, int num2) { return num1 + num2; }

Into
int add_nums (double num1, double num2) { return to_int (num1 + num2); }

The rules of refactoring are in place to make it a more pleasant environment for everyone to work within. If everyone is obeying them, the chances are greatly reduced of you logging in one morning to find none of your code working the way it did the night before.

Breaking the Rules


Sometimes it's okay to break the rules. Mostly this comes along with the access you have to fix the problems that come along. Imagine you are a young turk, looking to make your name with some great improvements to /mudlib/mixins/basic/desc.c. Someone gives you access to that code, and you decide 'Ha, set_short is too limited. I'm going to make it take half a dozen parameters, all of which will be mandatory'. That's not allowed, because it would break... well, almost everything. In this hypothetical situation, the only access you have is to that file directly, not every file that uses it. The rule is, 'if you break it, you fix it', and if you can't reasonably fix it, you don't get to break it. On the other hand, if you have write access to /d/game/united_kingdom/ and you want to break something in /d/game/united_kingdom/handlers then, with care, you can break the rules because you're in a position to fix everything that might be using the code. Sometimes refactoring is a task involving many objects, not just one object or one method. Additionally, you can use common sense to tell whether or not anything is likely to be using the method in question. If you have a method 'check_things' in an obscure room in an obscure area, then go ahead and change that method if you need to you'll only need to fix that one room after all. It's extremely unlikely that anything else will break as a consequence, even if it is a public method. Let the impact of change categories guide you if it's critical or high, don't do it. You'll break more things that you can realistically fix. If it's medium or low, then proceed with caution. Just be prepared to fix anything you may break. 434

Epitaph Online

http://drakkos.co.uk

Sometimes, although the situation is rare, someone needs to change something critical even though it is almost guaranteed to break other objects. When add_action was removed from the driver, or when type-safe checking was implemented this was changing driver code, so it impacted every object in the game. If something has to go, then it has to go. There's a process that you go through when this is the case, and it's something like this: Announce the deprecation of a piece of functionality. Provide a list of objects that will need to be fixed. Post a deadline by which the code must be changed, along with guidance as to how changes should be implemented. Be on hand to help with this. When the deadline is reached, give a few days' notice before you make the changeover. Give a date at which time the change will be made. Don't say 'in the next few days', be specific. Make the change, even though things will probably break. Fix the things that broke.

This is a time consuming process, and one that we don't go through very often because it's a big hassle for everyone involved. It's only permitted in extreme situations, and if you have to ask yourself whether you have the authority to make such a change, the answer is you don't.

Refactoring
So, what kind of things do we do in refactoring? There are several, but the most common things are: Removing dead code Making inefficient code more efficient Making code more readable Making code more maintainable

Ideally, refactoring is a proactive process you do it as an ongoing part of development. In reality, we tend to refactor only when there is a problem with the code with which we are currently working. Refactoring is normally a first step towards adding new functionality. When we refactor, we are looking to make the code look the way it would have done if it were written properly the first time. Refactoring can be as simple as changing the name of a variable to something more meaningful; however, if this is a publicly-accessible variable, even that trivial change can break code. Sometimes, it's a case of improving the aesthetics of an object. Said object may work perfectly, but offend the sensibilities when the code is viewed. The aesthetics of code are important they are usually a hint as to where refactoring can profitably be applied. Complex, unwieldy, and ungainly coding structures are usually there to handle complex logic operations that could potentially be either extracted or remodelled. However, you shouldn't automatically think 'complex 435

Epitaph Online

http://drakkos.co.uk

code is bad code', especially if the cold is old. Some code isn't ugly, it's battlescarred it's been thumped to bits by countless rounds of testing, and then patched up and fixed and put back into the field. Recognising the difference between ugly code and battle-scarred code gets easier with practise.

Some Common Refactoring Tasks


There are several common tasks that are done to refactor objects. Some of these are structural, relating to the way in which objects are connected to other objects: Generalising object functionality. Specialising object functionality. Improving encapsulation. Lowering impact of change

Some of these are related to the code inside objects: Simplifying internal structures. Improving variable names. Simplifying logical comparisons Substituting one algorithm for another Consolidating conditionals Extracting functionality into separate methods. Reducing inconsistency in naming and parameter ordering

Martin Fowler (http://www.refactoring.com/catalog/index.html) has a list of common refactoring tasks. Not all apply to Epitaph, but you can get a taste of what refactoring is all about.

Conclusion
This chapter isn't a technical resource about refactoring, but the outline of a philosophy for refactoring that can reduce tension amongst your colleagues. Refactoring is an important and on-going process, one that you will undoubtedly get involved with at one point or another. Its role in this material is to outline a set of criteria by which you should refactor a 'manifesto of courtesy' for how to make sure you don't inconvenience everyone with your changes.

436

Epitaph Online

http://drakkos.co.uk

Coding Etiquitte
Introduction
Formal codes of etiquette exist to smooth relationships between people by establishing boundaries of acceptable behaviour. So it is in coding within a multideveloper environment there is an etiquette that works to reduce friction. As far as I am aware, no-one has actually formalised this before, so this is my own clumsy attempt to do for coding what Emily Post did for the 1920s. It's very easy to step on toes as a developer, and having a little consideration for your colleagues is the best way to show the necessary respect for their time and effort. I don't think there's a lot of value in having an exhaustive encyclopedia of such rules, so I have concentrated only on those of real importance to the process.

Before You Write Any Code


The first steps you take to ensure you are being polite are taken before you write any code at all Check for Duplication of Effort To begin with, look to see if there has been any duplication of effort. One easy way to cause Conflict is to say 'Hey, I'm coding this cool thing here', when someone else is already coding that cool thing elsewhere. Additionally, check to see if there's any abandoned work that has been done in the past on similar developments. Sometimes projects stop simply because people don't have enough time, rather than because they weren't working out. A rescue and adaptation of old code can be as valuable and efficient as writing the code from scratch. Make Sure All Involved Parties Are Consulted An easy way to rub people up the wrong way (and to run the risk of your project being mothballed) is to not consult the people who need to be consulted. You can't just say to yourself 'I'm going to write a faction that is full of pirates!', because the placement of factions is a strategic decision for the game, especially if they involve something that might potentially impact on a future game feature. Making sure that the relevant parties in those domains are consulted before you start coding is the easiest way to avoid potential future conflict it's hardly ever the case that you're told 'No, you can't do anything like that', but there may be conditions, or modifications that are necessary, or perhaps a recasting to fit in line with future domain objectives. Just a post on a board is appropriate for the most part - keep people in the loop. 437

Epitaph Online

http://drakkos.co.uk

Ensure a Migration Strategy If you're remodelling code that's already in the game, or code that's likely to impact on other developers (such as a change to inherits or handlers), then make sure you plan in advance to make the change with the smallest possible 'interruption of service'. Make sure everyone knows, ahead of time, what's going to happen and what that means for their own code. Do it first, because it'll already be too late if someone points out a problem with your migration strategy after you have made the changes.

When You Are Writing Code


The biggest area for potential conflict is in the code you actually write all code on Epitaph is very tightly interconnected, and you can easily cause problems for other developers if you write code without care. Be Wary Of the Impact of Change As per our discussions on the impact of change, you need to bear this in mind when you are changing code. If it's going to change the way the code functions, don't do it unless you've gotten people on board and given warning. The higher the impact of code, the greater your consultation with others should be. Seriously, don't be a dick don't just break other people's code because it's convenient for you to do so. Write Your Code Cleanly There exists, at least in my mind, a bell curve that describes the simplicity of the code written by developers. I call it the Obfuscation Curve:

Newbie developers write simple code because it's all they know how to do. However, as the years go by, they accumulate knowledge of new tools, techniques, and syntax. They then fall into the incredibly common trap of thinking 438

Epitaph Online

http://drakkos.co.uk

that a good developer is defined by the number of tricks they know. These tricks then tend to make their way into every piece of development, just to show how 'clever' the coder is. Additionally, developers at the midpoint of experience tend to associate complexity with quality if the code is clever and does what it is supposed to do in a highly efficient (albeit inscrutable) way, then it must, by definition, be good code. This couldn't be further from the truth. There is some merit to the idea that good code can be an intellectual exercise doing things in new and unusual ways is personally satisfying after all. However, in a multi-developer environment you are actively retarding development by making every developer who follows you puzzle out the logic of your code. Your lapse into egotism is a burden to all. I don't care what you do with your solo development projects, but when you're working with others, don't do it. As developers gain further experience, especially after working with other people, they start to return to the idea of clean, simple code. It's much more maintainable, much more readable, and the minor efficiency losses are more than compensated by the ease with which fellow developers can add features or address problems. Really beautiful code, and there are some lovely examples of this, is elegant. Elegance demands simplicity of expression. The developers with the best Code Fu write elegant code, not complicated code. As a counterpoint to this, I don't mean to say that you need to write code that even the newest creator can understand. You can make available to yourself the full vocabulary of the programming language when I talk of readability, I mean readability to a fellow developer of reasonable literacy with the language. For example, the following code is needlessly obfuscated:
if((check = ::move(dest, messin, messout)) != MOVE_OK) return check;

You can follow what it does, but it doesn't need to do it so awkwardly. There's nothing lost by being explicit in the code:
check = ::move (dest, messin, messout); if (check != MOVE_OK) { return check; }

You lose nothing, and you gain readability in exchange. To show that I'm not simply having a go at other people here, I'll include an example from my own code:
tot = map (filter (property_list, (: member_array ($1->street_name, $(monopoly_sets)[$(m)]) != -1 :)), (: $1->houses :));

439

Epitaph Online

http://drakkos.co.uk

Sure, it's nice Code-Fu, but it was written eight years ago when I too confused complexity for cleverness. But I bet it would take you quite some time to work out exactly what this line of code is supposed to be doing. I'll give you a hint, though, it's doing something quite simple. Document Extensively Good code is its own documentation. I am in no way a proponent of 'commenting metrics', whereby X lines of code must have Y lines of comments. As long as you pick meaningful variable names and don't over-complicate it, you'll find that your code is readable enough. Everyone has a different opinion about readability, though. I've occasionally found that someone's gone to the trouble of commenting code that I was too lazy to comment myself. They have my thanks for this! However, whenever you're doing something a little bit exotic, you should outline what your intentions were within the code. Don't describe what the code does, describe why it does it. The following would be a bad comment:
// // // // Does a map on the filtered property_list array. The filter filters the array for all those streets that are part of the value $m in the monopoly_sets mapping. It returns the houses member of the class in array format.

tot = map (filter (property_list, (: member_array ($1->street_name, $(monopoly_sets)[$(m)]) != -1 :)), (: $1->houses :));

That explains what the code does, not what the intention of the code is. You can't tell from that comment whether the end result is what the code would suggest. A better comment would be this:
// This piece of horrible code gives an array of all the houses a player // has in the properties that belongs to a set.

That explains what the code was supposed to do, so that a developer can look at the end result and decide whether or not that's what happens. Comments should explain intention, not simply dissect the code. Attribute Contributions Coding is a collaborative effort, and much successful coding is simply welltargeted plagiarism. That's absolutely fine if someone has already solved the problem you are having with a bit of their own code, then use that code as a template for your own. However, when you do this, it's very nice if you can provide an attribution, such as:
// This code borrowed from Drakkos' Killer Weasels

Attributing the work of those who have gone before you is a respectful activity it doesn't take away from you as a coder, it enhances your reputation in the eyes of 440

Epitaph Online

http://drakkos.co.uk

other people. Moreover, it makes it easier for people to maintain the MUD - if I know that you based a piece of code on a function in /d/game/stupid_thing, then I know that if I fix your code I may also have to fix it there.

When You Have Written Code


Once you have written code, there are a few more things that fall under the general criteria of politeness. Abdicate Ownership Once the code is written, you should mentally hand it over to the MUD (we actually have a code ownership policy that requires that). It's no longer your code it's production code, belonging to everyone. It's a mental activity, so you don't need to actually do anything for this... but if you get wound up or upset by someone else changing something in your code, then you're doing it wrong. Complaining that someone else 'changed your code' is rather rude in an environment where everyone else is being a team player with the code they provide. Be Willing To Maintain Even though you abdicate ownership of the code, you are still the person best qualified to maintain it. As far as possible, you should keep an eye on reported problems with your developments and be prepared to fix them, especially if they flummox other creators. Although you aren't the owner of the code, you are the expert on it. Make Sure All Parties Have Adequate Information There's little worse from a 'creator satisfaction' perspective than those with a need to know not being told what they, well, need to know. Need to know parties will vary from project to project, but if it's an area going into the game then the liaison domain need to know as much as you can tell them about features that may potentially go wrong. If it's a cross-domain collaboration, then all the collaborating domain administration teams need to know the details. A post on the game board is the usual mechanism for announcing a new feature for the game, but a mail to the relevant parties ahead of time also shows the appropriate amount of respect for your collaboration partners. Something like, 'Hey, we're going to make this live next week, so let us know if you have any lastminute comments' is a great way to make sure everyone gets a chance to have a last look over the development before everyone is committed.

441

Epitaph Online

http://drakkos.co.uk

Conclusion
There aren't all that many rules you need to worry about and most of them are fairly self-explanatory. They all stem from a single basic concept though don't make life harder for people than you actually have to. Working with other people means being able to compromise and communicate clearly. It's much easier when everyone has a firm idea as to what is acceptable and what is not when making development decisions. Some of us are better (or worse) than others at being polite and respectful developers. It is not unknown for a developer to wake up one morning to find that everything they have written has been broken because someone else 'fixed' a low level inherit. Such occasions are rare, but there are precedents. Simply bear this in mind how would you feel if you logged on one morning and everything you had written had to be changed, without any warning or consultation? You'd be legitimately pissed off. If it would bother you, don't do it to other people.

442

Epitaph Online

http://drakkos.co.uk

Source Control
Introduction
One of the most useful systems we have in place on the MUD is our source control system. The MUD uses a system called RCS the Revision Control System to ensure that multi-developer collaboration proceeds as smoothly as it can. It is a system for restricting the access to make code changes so that only one developer modifies a file at a time. This neatly gets around the problem of one developer overwriting the changes of another developer something that, with the best of all intentions, is quite common when no formal system exists to prevent it.

Source Control in the Abstract


Imagine you are a Epitaph developer. That should be quite easy, because presumably you are! You are working with a file, /d/game/awesome.c. You download a copy of this to your local machine, and make some changes. While you are doing this, unaware of the fact you are working with the file, I come along and download it to my local machine and start making changes. You finish up, and upload the file to the MUD. I finish up, and then upload my file to the MUD. Despite the fact we both made changes, my version overwrites yours. There are ways to minimise the chances of this for example, like so:
(cre) Drakkos: Hey, I'm about to do something with /d/forn/awesome.c (cre) AnotherCreator: Can you hold off a bit, I'm currently working with it. (cre) Drakkos: Sure!

This is imperfect though it doesn't catch anyone who is offline and working through FTP, and it doesn't catch anyone who isn't reading the channels. Communication can go some way towards making sure problems like this don't occur, but it doesn't solve the root issue. Source control steps in and provides the solution to this problem. All files on the MUD can be placed into source control (this has to be done manually, because sometimes it's inconvenient), and once they have been registered with the system it becomes impossible for anyone to make changes unless they first 'check out' the file. Only one person at a time can check out a file, which means that if you are working with awesome.c, I will get an error message if I try to make any changes. When you're done, you 'check in' the file and provide a little description of what you did. At that point, your changes are said to have been 'committed'. Part of the beauty of a system like this is it keeps track of the differences between the new version and all previous versions, and with a single command a developer can revert the file back to a previous version. If your changes to awesome.c 443

Epitaph Online

http://drakkos.co.uk

introduced some horrible, game destroying error... well, we just 'revert' the file back to its previous version and no harm, and no foul. It really is a wonderful system. In our example of you and I working with the same file, no-one has done anything wrong. It's not a sign of bad communication or a dysfunctional environment it's just One Of Those Things. While it's almost never a good idea to apply a technological solution to a social problem, what we're describing here is a technological solution to a technological problem. Like any system though, it's only as good as the people using it. We'll talk about some of the social problems with RCS later in this chapter.

The Epitaph RCS System


If you are the sole developer working on a project that is not in the game, you may find it easier to keep everything off of RCS while you work. It can be inconvenient to continually have to check files in and out of the system, and somewhat against the spirit of the thing to check them out and never check them back in until the development is completed. When the code goes live, or into playtesting, you should without exception add the files to the RCS system. Luckily, it's very simple to do you use the rcscreate command:
rcscreate /w/your_name_here/deadville/rooms/*.c

You'll be prompted to enter some text as a matter of convention, this text is usually 'Initial Revision'. Once you've entered the text, that's it your files are now on the system and you won't be able to make any changes until you first check them out. That's done using the rcsout command:
> rcsout /d/support/deadville/chapter_02/rooms/street_01.c /d/learning/deadville/chapter_02/rooms/RCS/street_01.c,v --> d/learning/deadville/chapter_02/rooms/street_01.c revision 1.1 (locked)

Now you have access to change the file, and nobody else does. When you've made your changes, you use the rcsin command:
rcsin /d/support/deadville/chapter_02/rooms/street_01.c

When you do this, you'll be prompted to enter some text. Please provide something useful and meaningful for this, because it's what people will see when they look at the log of changes that have been made. You shouldn't detail the code you changed that's available already (more on this later). Instead, your comment should focus on intention and what the change was supposed to achieve. If you haven't actually made any changes to the file, it will automatically revert to the last version:
d/learning/deadville/chapter_02/rooms/RCS/street_01.c,v d/learning/deadville/chapter_02/rooms/street_01.c file is unchanged; reverting to previous revision 1.1 <--

444

Epitaph Online

http://drakkos.co.uk

If, after having checked out a file, you decide that you don't actually want to make any changes, or if you've made changes and they're not what you want, you can release your lock on the file using rcsrelease. This will release your claim to the file without committing any of your changes. It'll revert automatically to the most current version of the file:
> rcsrelease /d/support/deadville/chapter_02/rooms/street_01.c d/learning/deadville/chapter_02/rooms/RCS/street_01.c,v --> d/learning/deadville/chapter_02/rooms/street_01.c revision 1.1 (unlocked)

If you want to see what files you have locked out, the mylocks command is your friend:
> mylocks You have the following files locked: /cmds/combat_position.c /cmds/creator/rehash.c /cmds/creator/req_uest.c /cmds/fighting_base.c /cmds/gather_base.c /cmds/guild-race/adventuring/assess.c /cmds/guild-race/adventuring/deactivate.c /cmds/guild-race/adventuring/inspect.c /cmds/guild-race/adventuring/judge.c /cmds/guild-race/adventuring/pick.c /cmds/guild-race/adventuring/probe.c /cmds/guild-race/adventuring/repair.c /cmds/guild-race/combat/cleave.c /cmds/guild-race/combat/flurry.c /cmds/guild-race/combat/reload.c /cmds/guild-race/combat/slam.c /cmds/guild-race/crafts/assemble.c /cmds/guild-race/crafts/cut.c /cmds/guild-race/crafts/facet.c /cmds/guild-race/crafts/fell.c /cmds/guild-race/crafts/harvest.c /cmds/guild-race/crafts/mill.c /cmds/guild-race/crafts/mine.c /cmds/guild-race/crafts/prospect.c /cmds/guild-race/crafts/smelt.c /cmds/guild-race/crafts/survey.c /cmds/guild-race/medical/bandage.c /cmds/guild-race/medical/culture.c /cmds/guild-race/medical/diagnose.c

Whoops, I should probably check some of those back in! You can also check to see which files another creator has locked out with mylocks:
> mylocks ploosk Ploosk has no files locked.

Wow, way to make me look bad. If you want to see who has a lock on a particular file, that's what the rcslocks command does: 445

Epitaph Online

http://drakkos.co.uk

rcslocks /www/wrapper.c File /www/wrapper.c locked by drakkos.

Finally, if you want to see the changes that have been made to a file, the rcslog command gives you all that information:
> rcslog /secure/master.c RCS file: secure/RCS/master.c,v Working file: secure/master.c head: 1.5 branch: locks: strict access list: symbolic names: keyword substitution: kv total revisions: 5; selected revisions: 5 description: Initial revision ---------------------------revision 1.5 date: 2011/01/30 16:48:00; author: drakkos; state: Exp; lines: +2 -2 Permission fixes, and some adjustments to apply_unguarded ---------------------------revision 1.4 date: 2010/12/22 20:17:28; author: drakkos; state: Exp; lines: +4 -4 Forcibly released due to inactivity ---------------------------revision 1.3 date: 2010/03/08 15:20:02; author: drakkos; state: Exp; lines: +3 -3 Updated to the new paths ---------------------------revision 1.2 date: 2010/02/13 11:58:14; author: drakkos; state: Exp; lines: +2 -2 Updates ---------------------------revision 1.1 date: 2010/01/15 02:51:04; author: drakkos; state: Exp; Initial revision =============================================================================

We'll spend a little bit of time talking about these entries, because there's a lot of information in there. Let's take the last of these as an example:
revision 1.5 date: 2011/01/30 16:48:00; author: drakkos; state: Exp; Permission fixes, and some adjustments to apply_unguarded ---------------------------lines: +2 -2

The first piece of information we are given is the revision number of the file. Each time a change is committed, the decimal part of the version increase by one. This follows a general convention of software updates... it's possible for a revision to increase the whole number part, but nobody ever does it. The date is when this change was committed, not when the change was made. As such, there can be wide disagreement with the 'official record'... sometimes files remain locked out for weeks or months, and so the date of a revision bears no 446

Epitaph Online

http://drakkos.co.uk

relationship to when the changes were made. The author is the person who committed the change. The state tag is not something we use much on Epitaph, or indeed use at all. It relates to the state of the release EXP stands for 'experimental'. The state can be anything, although the convention for other states is STAB (for stable) and REL for release. You'll be unlikely to encounter anything other than EXP though as you work your way through the Epitaph codebase. Lines indicates the net number of lines that were added (defined as any line that was changed) and lines that were removed (defined as any life that is no longer in the code). The most important bit of all of this though is the text that accompanies the entry this is the text that the creator entered as part of the rcsin. If good practise is being followed, this will be a meaningful description of the change that was made. The final command you're likely to make use of is rcsdiff this gives you the exact difference between two version of a file, showing exactly what was added and what was removed. If I wanted to know what changes were made between version 1.2 and version 1.1 of a file, it's this command I use:
rcsdiff -r1.2 -r1.1 /secure/master.c

This will give the following output:


< * $Id: master.c,v 1.2 2010/02/13 11:58:14 drakkos Exp $ --> * $Id: master.c,v 1.1 2010/01/15 02:51:04 drakkos Exp $ 220c220 < return 0; --> return "/d/liaison/master"->query_deputy(person);

It can be slightly difficult to read this output. Lines marked with a < are lines that have been added, and lines marked with a > are lines that have been removed. In essence, this line:
return "/d/liaison/master"->query_deputy(person);

Was replaced with the following:


return 0;

There are more commands available as part of the RCS system, but these are the commands you'll be working with most often. The help-file for RCS will outline some more interesting and useful options.

447

Epitaph Online

http://drakkos.co.uk

Problems
There are, as usual, social problems that come along with any system. While our use of RCS is on the whole very good, there are lapses usually centred around specific individuals. Alas, I count myself amongst these senility has grabbed hold of me in my old age, and I thus often forget I have files locked out. And then, when I check them back in, I forget what it is I have done. Usually this is a result of carelessness rather than malice files remain locked out for as long as it takes for someone to realise (for example, another person who needs access to the file). However, there is a more insidious problem of people pre-emptively checking out code so that other people can't change 'their code'. This, as we have discussed, is not a mindset we like to encourage. The rcslog of a file is a historical record it shows what changes were made, along with a short summary. However, this record can be constantly interrupted with 'noise' such as files being forced (this is when someone forcibly checks in a file for you this is something available only to senior creators and above), or less than helpful rcs entry messages. For example, from /secure/simul_efun.c:
---------------------------revision 1.6 locked by: drakkos; date: 2010/02/13 11:57:27; author: drakkos; state: Exp; lines: +4 -5 Updates ---------------------------revision 1.3 date: 2010/01/17 23:14:52; author: drakkos; state: Exp; lines: +2 -1 Fixes ---------------------------revision 1.1 date: 2010/01/15 02:50:02; author: drakkos; state: Exp; Initial revision =============================================================================

None of these are useful messages - they give no useful information about what has actually been changed, and people will need to go through the rcsdiff output to tell. This is a problem with the people using the system, not the system itself. Source control is not a substitute for a good developer environment it stops accidental collisions of code, but it won't help with genuine social problems between developers. Note too that once a file has been put on RCS, it can't be removed except by a Trustee. That means no shuffling files around, or deleting them permanently. You need to be sure that you can commit to what's there unless you want to risk the wrath of waking a Highlord from their blissful slumbers.

448

Epitaph Online

http://drakkos.co.uk

Conclusion
Source control is one of the most important systems we have for supporting our development work. It means we don't need to keep backups, or tediously roll back changes by hand. It means we can track changes made to objects, and identify people who were responsible for making changes. All of this in addition to its core function of making sure that we don't end up killing each other over code collisions. Learn to love it!

449

Epitaph Online

http://drakkos.co.uk

Documentation
Introduction
Writing documentation is one of the least enjoyable tasks that comes along with developing code. As such, it tends to be something that's left until the last minute, or done in infrequent, unreliable pushes of effort. It's a shame it is so tedious to produce because good documentation is worth its weight in gold for those who come after you. When it comes to documentation, I don't necessarily mean commenting. I am not a proponent of the view that comments should form X% of your source code (although many people are) because I believe that good code is its own documentation. In addition to comments that describe what code is supposed to do, Epitaph has a commenting format that allows for automatic extraction and indexing of object functions, their return values, and their parameter lists. In this chapter we will also talk about the format used by the MUD's help-files, and how you can aid in our documentation effort by migrating user help into bespoke object help-files.

Commenting
The usual argument is, 'It is good practise to comment your code'. In my experience, when this argument is followed to its logical conclusion it actually detracts from readability. Imagine the following code (and this is not an exaggeration, I have seen files like this by the dozen):
// This declares an integer variable called num. int num; // This declares a string variable called name. string name; // Create a for loop with a counter variable called i. // It will loop while i is less the num. for (int i = 0; i < num; i++) { // Send the text that is in the variable name to // this_player(), using the tell_object() method. tell_object (this_player(), name + "\n"); }

The problem with these comments is that they are aimed at the wrong audience. You shouldn't write comments so that your grandmother, or your mother, or your best friend can understand what is going on. You should write comments so that a fellow literate programmer can know what is going on. These comments do not say anything that the code itself doesn't say. The code itself gets swamped in the comments, making it a little less readable. Nothing is gained from this, even though everything has been commented. Good code is its own documentation. The following is bad code: 450

Epitaph Online m=i+((r*i)-d);

http://drakkos.co.uk

While you can work out what this is doing, there is no hint as to why one variable is being modified by another in a particular way. On the other hand, simply choosing meaningful variable name turns that into self documenting code:
my_money = income + ((reserves * interest_rate) - debits);

In this code it is obvious what is happening there's no need to comment this. You'd certainly need to comment the former. Sometimes though, even with meaningful variable names, you're going to end up doing something a little bit esoteric. Whenever you feel that it is unlikely that a fellow literate programmer will be able to tell, at a glance, what you were trying to do that's the time to add a comment. This gives a happy balance between enhancing readability and not restricting you from coding productively. However, even assuming complete comprehension of what each individual line of code is doing, it is hard to tell, 'at a glance' what the big picture is. That's where the system documentation comes in we provide documentation on each of our functions so people can tell what they put in, what comes out, and what the value that comes out will mean.

Commenting Good Practice


One of the biggest problems with large bodies of commenting is the difficulty in keeping it up to date. What tends to happen is that the code gets changed and the accompanying comment doesn't get updated. Before too long, the comments bear little relation to the functionality and become actively unhelpful. That's why it's important to document the intention, not the actual steps taken. You should also try to document 'why' along with 'what' why did you decide to do something one way over another? Any time you had to spend a bit of time puzzling over alternatives, you can save those who follow you the effort by saying 'I decided to do it this way because it's more efficient/maintainable/readable than the other way'. If you are making any assumptions at all in your code, then for the love of god document those assumptions. If the entirety of your function assumes that a particular parameter is within a certain range, make sure that information is documented somewhere other than in your head. Of course, if you are going to rely on such things you should have a guard condition in the code ensuring that the function won't be executed if the parameters are invalid. Still, document the assumptions you make. Avoid being humorous in comments if it comes at the expense of clarity. Don't use code words, or in-jokes, or obscure references, no matter how widely understood you believe the reference to be:

451

Epitaph Online // This fubars the string. string do_fubar(string str) { }

http://drakkos.co.uk

Finally, don't comment out obsolete functionality delete it entirely. The revision control system means that the functionality is available should it be required (oh make sure the file is on RCS first!), and removing it entirely from the code greatly increases clarity.

Autodoc
Epitaph has a commenting convention based on the Javadoc standard. It's called Autodoc, and it integrates documentation for functions into the standard help system. Imagine you wanted to know what net_dead was for in /global/player you can find out by typing 'help net_death', and you'll get a little help-file discussing it:
net_dead net_dead Name net_dead Called when the player goes net dead. Syntax void net_dead() Defined in /global/player.c Description Called when the player goes net dead. This is called by the driver when a player goes net dead. Turns them into a statue and stuff. Epitaph creator help

That help-file is generated automatically from the comments that have been put before the function in the file. As long as the comments adhere to a particular syntax, they can be parsed and made available to everyone. You are unlikely to ever need to do this for rooms, NPC and specific items but if you're doing anything more substantial, it's very useful if you can provide autodoc commenting. An autodoc comment starts with a special code: /** Every line that follows begins with a star in line with the first of the asterisks, and it ends with the normal closing of a block comment: */.
/** * * */

452

Epitaph Online

http://drakkos.co.uk

The first line of text is what is used for the summary that follows the name of the function. The rest of the text is used for the description of the function. You can mark this up with normal HTML, so you can add in paragraphs and line-breaks as necessary. The syntax of the function is extracted automatically by the autodoc handler, as is where it is defined. The rest of the information we need to provide, and we do this using autodoc tags. These begin with a @ symbol, and are interpreted by the autodoc handler according to the text that belongs to the tag. For example, let's take a simple function from /d/support/ master.c and put it through the autodoc format. The function is this:
int set_project(string name, string pro) { if (geteuid(this_player(1)) != query_lord()) return 0; return ::set_project(name, pro); }

Its task is simple it checks to see if the person making use of the function is the lord of the domain. If they aren't, it returns 0 and does nothing. If they are, it passes responsibility onto the object that this object inherits. First, let's describe that in an autodoc:
/** * This function sets the project of a domain member. It first checks to * see if the person making the call to the function is the lord of the * domain. If they are not, it will return 0 indicating failure. The * method will make a call to the set_project of /std/dom/base_master.c * if this initial check is passed. * */

Next, we add in tags to provide an explanation of what the parameters and return value mean. We have to be careful with formatting here there should be one space between the asterisk and the tag, or it won't be picked up by the handler. @param is used to give a meaningful description to a parameter, and @return is used to describe how the return value should be interpreted.
* * @param name The name of the person for which we want to change the project. * @param pro The project the person is to have in the domain. * @return 1 if the project is successfully changed, 0 if it is not. *

There are some other valuable tags we can provide:

453

Epitaph Online

http://drakkos.co.uk

Tag @see @example @ignore

Description Adds a reference for other objects of interest. You can use this to direct attention towards related objects. You can use this to provide a code example of the object in use. Makes it so the autodoc handler ignores the function for the purpose of automatic generation. This is useful if it's a small, private function that no-one need worry their pretty little heads about

We should definitely add in one of each of the first two;


* @see /mudilb/inherits/dom/base_master.c * @example * ret = set_project ("drakkos", "Being Awesome");

This would give us our full autodoc comment:


/** * This function sets the project of a domain member. It first checks to * see if the person making the call to the function is the lord of the * domain. If they are not, it will return 0 indicating failure. The * method will make a call to the set_project of /std/dom/base_master.c * if this initial check is passed. * * @param name The name of the person for which we want to change the * project. * @param pro The project the person is to have in the domain. * @return 1 if the project is successfully changed, 0 if it is not. * @see /std/dom/base_master.c * @example * ret = set_project ("drakkos", "Being Awesome"); */

This will generate the following help-file for help set_project:


set_project Name set_project Syntax int set_project(string name, string pro) Parameters name - The name of the person for which we want to change the project. pro - The project the person is to have in the domain. Returns 1 if the project is successfully changed, 0 if it is not. Defined in /d/support/master.c Description This function sets the project of a domain member. Epitaph creator help set_project

454

Epitaph Online

http://drakkos.co.uk

The

This function sets the project of a domain member. It first checks to see if the person making the call to the function is the lord of the domain. If they are not, it will return 0 indicating failure. method will make a call to the set_project of /std/dom/base_master.c if this initial check is passed.

Example 1 ret = set_project ("drakkos", "Being Awesome"); See also /mudlib/inherits/dom/base_master.c

Cor, don't that look purty? As a matter for convention, you should also add such a comment at the top of the file detailing, at the very least, who the author is and when it was started. If you're working with a legacy file, then that might actually be known:
/** * Support Domain Master Object * @author Who Knows * @started A Long Time Ago */

Still, it's better than nothing. Marginally, anyway. Autodoc works for function level documentation. For commenting within a function, standard commenting is all you need to use.

The Autodoc Process


Once you've written a file that you want to add to the autodoc system, you have to add it using the autodocadd command:
autodocadd /d/support/master.c

A second or so later, your file is in the system. However, it won't appear when you try to get the help file. Help-files are updated on a delay to reduce load on the system it will be generated at some point in the not too distant future. However, you can kickstart the process by using the autodoc command this will force the generation of the documentation:
autodoc /d/support/master.c

This will create an overall view of the file in /doc/autodoc/. The filename will be the same as the file path, except all the backslashes will be replaced with dots. Thus, it's the file /doc/autodoc/d.learning.master.c. Each of the functions that have been documented will be stored under /doc/creator/autodoc/- everything in here is organised in a familiar file hierarchy.

455

Epitaph Online

http://drakkos.co.uk

The one for set_project will thus be found at /doc/creator/autodoc/d/support/ master/set_project. We can force these files to be added into the help system using the rehash command on the directory:
rehash /doc/creator/autodoc/d/support/master/

Your help-file will now be available in all its glory, to everyone who needs it.

Other Help-Files
Player help-files on the other hand are deployed using data files - when we rehash the help category, the help handler will take all of these files, write them out into a format called nroff, and create the web-help for the concept/command/room/object. The helpfile for the forge command, for example, lives in /data/help/known_commands/forge.hlp, and looks like this:
::item "forge":: ::->title:: "forge" ::->dir:: PATH_KNOWN_COMMANDS ::->aka:: ({ }) ::->see_also:: ({ }) ::->bits:: ({ "Syntax", "$lit$" "forge <string> {from|with} <parts>", "Description", "The forge command is used to shape raw parts into a finished product, " "most often used for manipulating metal products. Which products you can" "create is handled by your access to schematic books, and the specific parts" "and skills required to create the item varies from schematic, material used," "and end product.", })

$lit$ is a special code used by the handler that says 'treat this like MUD output' the it makes sure that the commands and syntax helps are in a consistent format. The rest of the text of the helpfile works by alternating between heading and content - the helpfile this produces looks like this:
Epitaph Help Help Name Forge Syntax forge <string> {from|with} <parts> Description Forge Epitaph

456

Epitaph Online

http://drakkos.co.uk

The forge command is used to shape raw parts into a finished product, most often used for manipulating metal products. Which products you can create is handled by your access to schematic books, and the specific parts and skills required to create the item varies from schematic, material used, and end product.

There is no need for you to wrestle with the intricacies of nroff formatting - all your helpfiles should be deployed as data files.

Why Document?
Sad as it is, you probably won't be here when the End of Epitaph comes. That's true of almost everyone in the space of a few short years, absolutely everything can change. People who seemed like part of the scenery become merely part of your memory. What will go on though is the contribution you made to the game. The only constant in life is change the MUD is going to change around your code, and if your contribution is to remain in the game it's going to have to be written in such a way that it is possible for other to maintain it after your departure. There is a great saying that helps get the idea in your head 'write your code as if the person who maintains it after you is a homicidal maniac who knows where you live'. Knowing that guy is going to have to deal with your code, wouldn't it be nice if you could placate him with some calming, soothing, useful comments? Moreover, good documentation can serve as an aide-memoire for yourself when you write a lot of code, you're guaranteed to forget the older stuff. When you come back and look an incomprehensible mess a year or so later, you'll be in only a slightly better state than any creator coming to the code for the first time unless your code is well documented.

Conclusion
Good code is its own documentation, I can't stress that enough. Commenting done improperly is worse than no commenting at all comments can be unhelpful, misleading, or downright wrong. In the process they can drown out the source code amidst a sea of green. However, when done properly, they are immensely valuable to everyone who works with your code. The Epitaph Autodoc system is a powerful way of providing help for coding functions in a consistent way for small objects such as rooms and simple NPCs, it's safe to ignore it. For anything that is going to be used more widely, you need to be considerate of your fellow creators and thoughtful of the future maintenance duties that go along with making a lasting, maintainable contribution to the game. 457

Epitaph Online

http://drakkos.co.uk

Domain Integration
Introduction
Effective development on Epitaph is a complex problem to solve. It involves many different developers, with many different cultural backgrounds, with varying degrees of expertise in software development, spread across many time-zones. It's remarkable we ever get anything done, when you think about it. As the MUD has grown more complex, it has introduced a whole new set of issues that need to be resolved. In this chapter we're going to talk about a process called Continuous Integration, but we're not going to use it in the same way most software developers mean. Most of what Continuous Integration involves are things we don't actually need, or tools that make no sense in the context of Epitaph development. You can think of this then as a modified process for continuous integration. We almost always do this anyway, but it's worth discussing why this strategy is worth adopting when dealing with code files coming from multiple sources.

Multiple Developers the Traditional Approach


We've already spoken a bit about the cultural and technical barriers that come with working with multiple developers. Once those have been resolved, the problems don't go away it just reveals the existence of new problems. The comments I am going to make here don't necessarily apply to single developer projects, but as soon as multiple developers start working on the same code files, there comes a problem in terms of integrating this code together. The traditional approach in software development works something like this: Everyone writes their code in isolation, over a period of time. At some point, the project leader says 'Right, let's put everything we've written together to see it all work' Everyone pools their code, and links it all up. Hilarity Ensues Everyone spends the next week or so changing their code so that it all actually meshes together properly.

The length of time between code integration events directly influences how many errors will be experienced. Technically, these are known as integration errors, and they come from various sources. The key source though is assumption everyone assumes everyone else is doing things a different way. Often, the problems aren't as obvious as files not loading the errors can be much more subtle. You probably remember the 1999 Mars Lander probe that went horribly wrong, costing NASA around $125 million. What you probably don't know is that the reason was because of an integration error. One team at NASA was writing their code using imperial measurements. Another was writing using 458

Epitaph Online

http://drakkos.co.uk

metric measurements. All of the internal error checking that was done at NASA failed to pick it up because each part of the program was actually working correctly. The problem came when the two pieces of code were supposed to work together. Partially this is a political problem if everyone decides on a standard to begin with and everyone sticks to it, the problem can be greatly mitigated. However, in large part it's a simple consequence of multi-developer work. People will make assumptions. One of the reasons why this is a problem is in the observed behaviour a failing probe, for example. Another reason is the simple stress and hassle of getting a project to work properly it can take weeks to resolve integration errors in a complex project (admittedly, the project for which this is true are usually a good deal more complicated than the typical Epitaph project) at a time when tensions are already high (integration is not a relaxing process). It can actually be bad for your health! It's certainly not fun, and that's what we're all here for. Epitaph operates a 'reuse' mentality rather than a 'roll your own' mentality. That's what all of our many inherits and handlers are for to make it so people don't need to reinvent the wheel. However, if someone in another project is making use of your code, and it suddenly breaks because of an integration error well, the last place people tend to look for the problem is outwith their own code. This is especially true if integration is an infrequent event the less frequently people integrate their code, the less likely people are going to assume that an integration error caused their new, baffling problems. The problem breaks down to the length of time between attempts to integrate the longer people go without bringing all their code together, the longer bugs and errors have to creep in.

Examples of this on Epitaph


This may sound like an abstract problem with little relevance to Epitaph, but one particular domain development strategy shows it in clear focus. It used to be the case for some domains that project code was developed in your /w/ directory, and the /d/ directory was only for finished code. Imagine that extended to the development of, for example, a whole city... in order for anyone to actually walk around the city, every room has to connect to the right directory in the right /w/ drive. The code can't make use of the armoury because the armoury doesn't pick up items in a /w/ drive. Everyone has to have workarounds and shoddy code just to make sure the areas work (like cloning objects from a /w/ directory rather than using the armoury). And then it's 'Integration Day', all the code gets moved into /d/, and absolutely nothing works properly. Everyone then has to spend the next week or so getting to the point where everyone thought they already were. People have inherits they have written that would be of use to others, but only their code 459

Epitaph Online

http://drakkos.co.uk

is using it because nobody knew it was there. One person has their move zone called 'Blah Zone' and the other has 'blah_zone'. Another person thought the connection to Awesome Street from Fantastic Avenue was a north/south exit, whereas everyone else was working under the assumption it was east/west. Multiply these problems (and others) by the size of the project and the number of creators, and you have yourself one massive headache. Solving the problems before they arise is always the best bet, but who can solve the problems across a dozen /w/ directories? Very few people have blanket write access to /w/, and while individual creators can grant permission to their /w/ directories, it's a lot to co-ordinate. If you're having a problem with your code and you need someone to help, they can only advise from the sidelines sometimes it helps if someone can just pop a few lines of code in place to show a tricky concept in situ. The problems of distributed and decentralized development get smaller with fewer creators, but they don't go away. Any length of time between integration events is going to cause integration issues.

Continuous Integration
Continuous Integration is how we solve this on Epitaph, and indeed our continuous integration is usually a good sight more continuous than 'real' programming. The philosophy is simple if the delay between integration events causes problems then the solution is surely to simply remove the delay. This is why large projects tend to work using 'live ammo'. When you get a project, you're told which directory in /d/ your project resides, and often there's already a skeleton in place so that people can actually walk around. The benefits of this are considerable. It gives context so people know where their code will fit into the larger arc of the domain. It ensures everyone is working with the right tools everyone uses the same inherits. It means that the handlers we use for in-game code can be used for development code. It also means that your project leader or domain administration don't need to go hunting through your things to check something for an update, they just wander to where the code is supposed to be. For large projects (the development of Dunglen, as an example), it is simply infeasible to do development any other way. The projects are too large, and there are too many creators working on them. Imagine the hassle if each creator had their street in their /w/ directory! There are a few problems that come with incorporating development code into a /d/ directory though. Mainly due to the fact that 'rough' code is living in the same directories as real code. One example of the problems this causes is in error stats - the reports from in development or playtesting areas tends to skew the perception of bugs across a domain - we take our bug counts seriously, and it's important when addressing maintenance and the triage of bugs that we have a reasonable understanding of the depth of the problem. 460

Epitaph Online

http://drakkos.co.uk

A scheme is in place to resolve the problems that come with live code existing with development/testing code. Directories that are under development should contain, as part of their path, the string _dev. For example, if you are working in /d/game/awesome_project and you want it to be marked as a dev area, you would change it to /d/game/awesome_project_dev. When it comes time to put the area into playtesting, it becomes awesome_project_pt. When it goes into the game, it becomes simply awesome_project. The code that is likely to have to make a distinction between play and development areas all have filename checks built into them. If you set a filename to have _pt in it, this also helps regulate certain PT capabilities, such as when and where PT protection may be switched on, and for how long. There is a second advantage that comes from this naming system it tests your integration. Changing the name of a directory should be as simple as changing a define in a path.h file, and everything should work flawlessly from that. If it doesn't, then you found out early. That's a good thing it's important you find this out before the code is due to go live! There are few things likely to upset your domain leader more than them saying 'Area awesome is now live!' and then finding out everyone needs to fix up the directories so they don't break.

A Framework for Area Integration


This is a problem that normally comes from areas since they have the largest number of discreet files and developers to go with them. As such, my comments in this regard will centre around a framework for area integration. When working with any project with a significant number of parts, it's important to have a number of 'utility files' in place so that area-wide behaviours can easily be implemented and changed. As a general rule if you're doing anything even remotely clever, try to break the functionality out into an inherit. Even if nobody ends up using it but you, it'll make it easier to fix bugs and add new features. You saw this yourself with the development of Zombieville. A new area should come with an architecture that permits easy integration everything should be using the same inherits for rooms and NPCs even if you don't see a need for one, create an inherit for these and have everyone use it. You'll be grateful for this when someone says 'Let's hook up this crime handler I wrote' and you don't need to manually alter two or three hundred files to do it. I tend to break out new developments into five directories: rooms chars items handlers inherits

Everyone should be storing things in the same directories a common repository, rather than for the village of Awesome to have its own chars subdirectory. If necessary, sub-directories under the base chars directory can be added it's just important everyone knows where to go and everyone is adhering to the same 461

Epitaph Online

http://drakkos.co.uk

standard. Where the directory is in your domain doesn't really matter very much The items directory however must be in the root of the domain if your domain is waterways, the items should be in /d/waterways/items/. This isn't a knee-jerk mandate the armoury looks specifically in that directory when building its lists, and if you have your items elsewhere they won't be made available. It's a functional necessity that it be a root domain directory. Again, sub-directories within here can help manage the mass of items that are likely to exist. When they go live, things like clothes, weapons and armours are moved into the relevant /items/ subdirectory. It's my recommendation that this is not done until the area enters the game. Comparatively few people have access to /items/, and it makes the task of debugging more difficult if access is not easily forthcoming. In your inherits directory, make a placeholder inherit for every type of room that will exist make one for shops, pubs, inside rooms, outside rooms, the whole works. It allows you to control the entirety of your development with only a few lines of code. Do you want to switch off XP gains while the area is in PT? You just need to set that up in the base level NPC inherit, and it's true throughout the entire area. Do you want to make it so nobody can portal? It's a matter of seconds to add the property to every room across the entire development. The convenience of changes like that cannot be over-estimated, and that's without talking about how easily you can add complex base-level functionality. Handlers can remain empty, but if and when they are written this is where they should live. Handlers have such wide-ranging impact that it's vital everyone knows what they are, where they are, and what they do. They shouldn't be hidden away in a subproject's sub-directories. It's not so much the framework here that's important, it's the fact that everyone knows where things should be. It greatly reduces confusion and improves integration across an entire development, and that's extremely important.

Conclusion
Developing in your /w/ drive may seem sensible, but it carries with it a penalty in terms of ease of integration. When working with multiple developers on a project, or even with multiple developers on a suite of related projects, integration issues can easily surface and cause disproportionate amounts of heartache. It is my suggestion that you adopt a process of continuous integration by incorporating your development into a large skeleton in the /d/ directory. This gives improved integration, a sense of context, and makes it easier for people to lend a hand with coding problems. The technical problems that come with an area being in a 'live' directory can be resolved using the _dev and _pt naming conventions. This has the added advantage of regulating the use of playtester commands in a sensible way.

462

Epitaph Online

http://drakkos.co.uk

463

Epitaph Online

http://drakkos.co.uk

Group Dynamics
Introduction
You and the other members of your domain form a Team. A team with a capital T! However, teams that don't have the right kind of dynamics tend to be problematic and cause issues for other teams in the developer base. Your domain should work well as a group, with everyone complementing everyone else to produce a whole that is greater than any one individual is capable. That only works if the internal dynamics of the group are such that individual interacts enhance, rather than detract, the efforts of others. At the same time, you are part of a larger Team that of the creator-base as a whole. There is sometimes a tension in your domain team versus the larger context within which that team operates. It's easy for a team to be insular and inward-looking rather than part of a larger, collaborative effort. In this chapter we'll talk a little about the dynamics of domains, and where you should be wary of treading.

What is a Domain?
Domains in Epitaph have existed since the start of the MUD. Partially they are an artifact of our top-down administrative structure, and partially they are a way of decentralizing the responsibilities of development and focusing people within a particular well-defined (or at least, hopefully well-defined) remit. Perhaps the most important effect of a domain though is the impact it has on creator cohesion - it allows the creators to form a productive group. A group is formed when certain elements are in place: A shared sense of identity A shared purpose A conformity to some set standards or hierarchy Individuals have clearly defined goals.

All of these things are in place for a domain the geographical (or abstract conceptual) remit gives a sense of purpose for the members of the domain, and the fact that everyone is working towards this gives a sense of shared identity. Each individual has (or should have) a clearly defined goal for most creators, this will be their project. For others, the goal may be more nebulous, but we'll talk about that. Each domain has its own standards and hierarchy, and partially it is this adherence to specific standards that defines a Game creator from a Support creator from a Cemtery creator. Every domain handles thing slightly differently, and those differences contribute to the sense of identify. There are different kinds of groups that exist, some of which are healthy and 464

Epitaph Online

http://drakkos.co.uk

some of which are troublesome. We'll talk about those a little later. Domains help us deal with problems that are otherwise intractable, as well as distribute out effort to ensure that all parts of our game world get creator attention. Domains restrict the development focus to a subset of the Epitaph. This greatly increases cohesiveness of development because people only need to be an expert in that subset. A Game creator doesn't need to be word-perfect on all the code in Support, and a creator for Support does not need to know anything about how Cemtery handles ambassadorships and retirements. In addition to the benefit of managing development, groups provide several highly useful social benefits. A good group provides a support network for its members everyone is part of the team and so it is to everyone's benefit if particular individuals are supported. If you are having problems with development or meeting your obligations as a creator, you will often find the members of your own domain are your first stop for support. One of the reasons for this comes from the previously mentioned shared sense of purpose this incentivises collaboration because a win for the team is a win for everyone. If the domain has a significant success (such as a new project being put into the game), everyone gets a little bit of that reflected glory. In the overall development direction of the MUD, it's sometimes hard to feel that your voice has any weight within the confines of your domain, your voice counts for much more because it's one of a smaller chorus. The exact amount of influence your voice has will of course vary from domain to domain, leader to leader and person to person but there are fewer people who have the authority to comment on the development of a single domain as opposed to mud-wide development. Having a sense of common ownership over a domain direction enhances the connections between group members. None of this is to say that domains are All Good All The Time. The domain structure carries with it a number of significant drawbacks, but these are almost all related to domain atrophy when a domain is no longer an active part of MUD development. This can occur for a number of reasons, but the most common of these is the disappearance of active leadership and a lack of development momentum. Most of the benefits of a domain come from the 'buzz' of working within a group of people if that buzz is not there, the domain can be a millstone rather than an energy boost. Individuals draw their sense of organizational norms from the people around them that's what people mean when they talk about organizational culture. If the norm in your domain is an active culture of development, then that's the lesson you'll take away. If the norm is for apathy and a lack of interest in what's going on, then alas that's what you'll tend towards also. Breaking these kinds of negative patterns isn't easy, but it's possible. We'll talk a little about that later.

465

Epitaph Online

http://drakkos.co.uk

When Is A Group Not A Group?


All domains are groups, but there are also ways in which groups can be subdivided. The simplest of these is to divide groups into either teams or cliques. You can think of teams as 'groups done right' and cliques as 'groups done wrong'. The difference really comes in terms of the inclusiveness of the group teams are inclusive and outward facing, and cliques are exclusive and inward facing. Teams foster collaboration within a domain and also integration into the larger community of the MUD. Cliques are hostile to outside 'interference' and prone to organizational dysfunction as a consequence. As a matter of collegial courtesy, our MUD is far more open than most development environments. Code is freely available, and the decisions taken by individuals are open for discussion. This is a healthy, albeit challenging, environment you don't get the benefits of secrecy in how you deal with things. Teams embrace this kind of environment, whereas cliques withdraw from it. The biggest differentiating factor of a clique is a sense of unity that members are 'better than' other groups. The 'cool kids' at school are a common example of the dysfunctional nature of a clique individuals within the clique are accorded higher status than those who are not within the clique. In order for members of a clique to be 'better than', then everyone else must, by definition, be 'worse than'. This attitude tends to manifest itself in bullying, or dismissive behaviour towards outsiders. In terms of integration within a larger MUD community, the inward nature of a clique tends to make it overtly hostile to outsider commentary. This is destructive to the fabric of collegiality that is at the core of Epitaph creating. First and foremost, the loyalty of a clique is to the clique. This loyalty over-rides the wider responsibilities of membership within the creatorbase, with all the attendant problems that brings. Often this loyalty is based on the presence of an 'Alpha Member' attended by one or more lieutenants. This lends itself towards a cult of loyalty in which pressure can be brought to bear on clique members to ensure a consistent attitude amongst members towards outsiders. The question then is, 'how can I tell if I am a member of a team or a clique?' It's hard to tell, from the inside it requires a willingness to be rigorously honest about your dealings with your fellow domain members. There are though some questions you can ask that help clarify the situation: Does the bulk of domain conversation occur in an inclusive forum? Are your decision-making processes open for comment to the wider creatorbase? Are differences with other creators resolved constructively and in the open? Is outside criticism taken as constructive and assessed for validity?

If the answer to any of these is 'no', then it is a warning sign that you may be in a clique. These are the features of a team that enhance creator-wide collaboration and inclusiveness. There are also features of a clique specifically that highlight potential dysfunction. Ask yourself the following questions: 466

Epitaph Online

http://drakkos.co.uk

Is there a tradition of loyalty to the group over loyalty to the MUD? Is there a tradition of covert sniping about professional disagreements with outsider individuals? Is there an obviously dominant alpha member who distorts collegiality with outsiders? Do significant portions of domain conversation occur in a private, exclusive forum? Is there a tendency within your group to react aggressively towards negative criticism?

Cliques share many of the features of constructive groups they foster a sense of community, shared purpose, and an incentive for collaboration. However, for all the reasons above, they cause a distortion of the positive atmosphere of open inclusiveness we try to foster across the MUD and create an atmosphere in which group-think can thrive (more on group think later). If you think your domain may be a clique rather than a team, the simplest thing to do is just voice your concerns the response that this gets will soon tell you the truth of the matter! If you are part of a clique, my advice is that you speak to an uninvolved director or admin as soon as possible and ask for guidance and support. Feel free to come to me if you want to discuss your worries on this score I am, after all, a Professional Outsider!

Group Roles
Each individual within a group has a particular role. Sometimes this role is highly defined, such as project or position in the administrative hierarchy. Sometimes it's a more nebulous, social role. Sometimes people take on more than one such role within a group. If everything is working effectively, you'll find everyone has at least one positive role they are playing in terms of group dynamics. Some of the roles that tend to be highlighted by group dynamic theorists are: Encourager Gate-Keeper Harmoniser Mediator.

A good group has people who exemplify all of these roles. The encourager is a cheerleader for the group, actively listening to ideas and encouraging participation from all group members. An encourager can motivate people on the fringes of a group to integrate more completely into the team. The gate-keepers are people with large stocks of bridging capital they help connect distributed subgroups within an organization. It's impossible in any large group of people for everyone to form cohesive bonds with each other, and gatekeepers allow for the lines of communication to be kept between disparate and unrelated clusters. 467

Epitaph Online

http://drakkos.co.uk

Harmonisers work to reduce tensions in a domain. No matter how well a group works, tensions will be encountered as a result of the friction of day to day work. Harmonisers help resolve those tensions and smooth over problems so everyone remains committed to the group goals. This has a great deal of overlap with the role of the mediator, who can work to resolve tensions by encouraging compromise. However, in dysfunctional groups there exist particular 'anti-roles' that actively disrupt the process of building good team dynamics. It's important that people know what these anti-roles are, and identify individuals who demonstrate those features. Knowing that a problem exists is the first step towards resolving it. Well known anti-roles include: Dominator Aggressors Deserter Recognition Seeker

The dominator is the person who feels as if they must monopolise every discussion, to always be the one to make all the suggestions and to mould the domain development strategy in the way that they intend. Group collaboration works only when everyone's views can be heard. Aggressors work to undermine alternative viewpoints by attacking them with unwarranted aggression. Aggressors discourage participation by adding an element of psychological trauma to putting forward an opinion. These two antiroles work together to produce an environment in which group-think can thrive coincidentally, these two anti-roles are most visible in groups that can be defined as cliques. Deserters are perhaps the most harmful of the anti-group roles. Deserters simply withdraw from the process entirely, and coast on the effort of others. This kind of behaviour is often referred to as 'social loafing' or 'free-riding', in which the individuals rely on the relative anonymity that being party of a group provides. When one is an individual working on a project, all the work can be attributed to that individual. In a group, the work is distributed so it's not so easy to identify who is pulling their weight and who is not. A social loafer is one who takes advantage of this to gain the benefits of domain membership without actually meeting their obligations to produce content. Finally, there is the recognition-seeker. Groups work best over the long-term if successes are shared amongst all members. The recognition-seeker subverts this by insisting on individual recognition for their actions. Everything on Epitaph is a group effort, but the recognition seeker seeks to put their name ahead of other contributions. You will probably recognize examples of all of these from people you have known and worked within the past. It's important in a successful group that anti-roles are discouraged and positive-roles enhanced. Talk with your domain administration whenever you feel someone is falling into a persistent anti-role within your team. 468

Epitaph Online

http://drakkos.co.uk

Group-think
Group-think is a feature in many team-settings, and is a consequence of individuals not being willing (or indeed being able) to put forward viewpoints contrary to those of the group as a whole. Such behaviour is endemic in cliques (because of the Top Dog mechanism) and in groups with large numbers of aggressors and dominators. As a result of alternative viewpoints not being put forward, a group reaches a premature consensus without actually analyzing the ideas that have been put forward in a suitably critical manner. The exclusionary nature of a clique also insulates it from external viewpoints in a way that can create a sense of 'false invulnerability' because competing viewpoints are simply not available. It is wrong to think though that this is a feature only in groups with systemic problems sometimes it can come from a group simply having too many people with the same kind of background good collaboration comes from having many people with different view points, not having many people with the same viewpoint. Groups that suffer from this tend to have an implicitly defined 'comfort zone' in which discussions can take place topics outwith the parameters of this comfort zone are usually not expressed or considered in the decision making process. Individuals do not wish to incur the wrath of an Alpha Member, or risk looking foolish as they are attacked by an Aggressor it's not the case that the conclusion is foregone, but there is an element of self-censorship applied by all members to the way in which they set the limits of their expressed opinions. As a result of this, even though individual agreement with a topic under consideration may be marginal, the overall impact of the social processes of the group is to suggest an overwhelming mandate because nobody has raised an alternative viewpoint. There is some fascinating research by a social psychologist by the name of Solomon Asch looking into conformity in group situations. The simplest experiment he did was known as the 'line experiment, and worked like this: The individual being observed was put into a group of between five and seven 'confederates' of the researcher. One by one, they were shown a card with a line on it, and then another card with three lines marked A, B and C. The participants were then asked to select which of these lines matched the length of the first line they had seen. For the first few trials of this, everyone would select the right card. In the next trial, the confederates of the researcher would then pick the wrong card. Astonishingly 75% of the subjects in the experiment conformed to what the group had selected at least once, even though their choice was obviously wrong. The social pressures of group are so intense that even in a group of strangers, there is conformity out of a a fear of looking foolish. However, one of the more encouraging things to come from the study was the counterpoint that if at least one other person goes against the prevailing group opinion, conformity rates plummet. The answer to group-think is to stand your group and speak your mind your alternative viewpoint could be the thing that brings out all the alternative viewpoints in everyone else. 469

Epitaph Online

http://drakkos.co.uk

Conclusion
Groups foster a sense of cohesion and domains are a powerful way to harness that cohesion for positive ends. However, the dynamics of groups are complicated and prone to dysfunction unless everyone is aware of how easily inclusive and effective teams can become insular and exclusive cliques. The most important thing to do when you are part of a clique is to recognize it for what it is and to make an attempt to break the dysfunctional elements. This can be hard, because by their very nature cliques can exhibit tremendous pressure on members. If you feel that you are not able to do so without support, then talk to someone outside of the clique for guidance. Some of this has a horrible air of 'if you're being bullied, tell the teacher', which is not how it is supposed to be interpreted. Social forces have an unbelievable amount of power in a closed society if you want some proof of this, I recommend checking out the book 'The Lucifer Effect' by Phillip Zimbardo. The power of bad group dynamics to influence individuals for the worst is such that these dynamics must be broken when they are observed. The first step in that is recognizing the problem, the second is getting the support you need to change the system.

470

Epitaph Online

http://drakkos.co.uk

Project Management
Introduction
Project management is one of the skills that any successful Epitaph creator is going to have to acquire. While there is a domain administration team who are responsible for managing the larger scope of a development, it is you and you alone who have the responsibility over managing your time to ensure that your code is developed in a timely and effective way. Project management may be a somewhat grandiose way to refer to this - it brings up thoughts of Gantt charts and costing models. I'm not going to talk about any of these systems in this chapter, because they are very dull and I'd rather chew off my own feet. What I will talk about is the self-regimen needed to deal with the complexity of day to day development. This is a far more interesting topic, and one that's actually possible to condense down to a single chapter. Those (few) readers who are interested in the formalised aspects of project management should consult... well... a therapist, I guess.

Project Management 101


Most projects are sufficiently large that they dwarf a novice creator. When you look at the number of things you need to code, it can be overwhelming. Unless it's a project of a handful of rooms, you'll find your ability to deal with the scope of the development is limited. A plan is called for! In the material for Being A Better Creator, we looked at creating a feature density chart in which we outlined each of the things our development would contain. A development plan is the same thing, except simpler and more granular. How granular you choose to make it is entirely up to you. Imagine a development plan for a simple room - a room has a number of sub-parts that can be done independently of each other: A room has long descriptions for day and for night A room has chats for day and for night A room has items for day and for night

Each of these is a 'task' for your project. When all of the tasks have been completed, then your project is done. As a principle, this is known as incremental development - each bit of the project is small by itself, but and you incrementally build your project by completing these small tasks. Defining what is a task and what is not is something that comes with experience of your own capabilities. My recommendation is that a task should be something that takes perhaps an hour or two of development time - that way you can easily slot it into whatever time you can spare for the MUD. It gives a sense of progress if you can tick off things that have been completed as you go along. They should 471

Epitaph Online

http://drakkos.co.uk

be big enough to give a sense of satisfaction as they are ticked off, but not so large that their sheer size is discouraging. Choosing tasks is a matter of individual preference -it's what makes logical sense to you as a subdivision of labour. Some people like to have individual rooms and objects as tasks, some people like to have things conceptually linked such as 'write room skeletons, write add_items for rooms, write chats for rooms). It'll be influenced mainly by your own way of approaching your development. There's no right or wrong way. Once you have decided on the tasks that go with your project, you'll need a way of keeping track of how far you are through each them. At the simplest end this can be done with a few columns on a spreadsheet, or in a notebook. However, if you want to make the results of your planning easily available to other interested parties (such as your domain administration team), you may want to consider making use of our project tracker software. There will be more on that later in this chapter.

Frameworks
Good projects have a framework that shows how everything links together, and the development is then attached to this framework as it is completed. The skeleton area we put together for Deadville is an example of a framework - it allows for a system of continuous integration amongst multiple developers. Any project you develop should have a framework, even if you are the only person developing it. Incremental development is the easiest way to ensure that this framework exists and that each development effort moves you closer towards your eventual goal. Consider something outside the normal examples of this material the Epitaph Oracle. It's partially web-based (the HTML front-end) and partially mud-based (the handler that works it, and the MUD-based front-end). The framework for this was to setup the simplest possible connection between the back-end and the front-end - a handler that had no functionality, and a front-end that had no interface. The first step in providing incremental development in this system is to add an option to the interface - for example, to list all questions. This then forces a further development in the handler (representing questions and then providing a list of them when queried). When that's done, there's the Epitaph Oracle. From that point onwards, you're just adding features. Imagine doing that the other way - writing the handler and then trying to put a front-end on it. Such a development would be ten times more difficult because of the scope of development. Before you can even see any kind of progress, you need to write all of the functionality that belongs to the handler. You then need to worry about how the interface is going to look and how it is going to communicate with the handler. Only when you have completed both of these tasks do you actually get to see the system function. Then when you have the both side of the system developed, you have to test the whole thing - if something doesn't work, where is it not working? Is it in the interface? The handler? The connection between the two? 472

Epitaph Online

http://drakkos.co.uk

Bite-sized development in a framework gives you instant cues as to whether your development is progressing along the right lines. This simplifies your development (you can correct faulty assumptions right away), eases your testing burden (you know where problems are to be found because they are in some small subset of functionality you just implemented), and motivates development (you can see this Mighty Organ grow under your gentle caresses).

Communication and Team Roles


Project management is all about communication - indeed, you've probably noticed that's a pretty major theme throughout this material. Much as within a domain, a project has a number of roles that must be fulfilled. Sometimes all of the roles will be fulfilled by one person - that's absolutely fine. It's also fine for one role to be filled by multiple people. However! If this is the case, communication is vital to ensuring that there is no overlap of authority. Domain Administration The domain administration team, unless they are an active part of the development, are usually 'hands-off'. Responsibility for ensuring thematic correctness and feature density belongs with the project leader, who is appointed by the domain administration. It is the domain administration who give the goahead for a project and also who will provide approval for the final set of features to be developed. It is also the domain administration team who will approve a project for entry into play-testing or the game according to domain standards. Project Leader The project leader in the development is the one who is responsible for setting the 'vision' of the project. They decide on the number and relative complexity of features, and how the project integrates with the larger context of the domain. That's not to say they are the only ones responsible for developing the plan they're just the ones that get the final say on what is to be put forward and what is not. The domain administration is the source of approval of the plan, but the project leader is the one who develops the plan for approval. For projects involving more than one developer, the project leader is usually the one with the most experience coupled with the best people skills. Project leading is more of a social activity than a development one, although it is unusual on Epitaph that a project leader will not also pitch in with the coding. The project leader is also responsible for keeping everyone motivated, and for making sure everyone is happy with what they are developing and knows how they are contributing to the overall effort. Nothing sows dissatisfaction easier than people not being sure why they are doing something. The project leader identifies developmental problems and puts in place solutions to deal with them.

473

Epitaph Online

http://drakkos.co.uk

Documenter The documenter in a team is the one who makes sure that the result of discussions is made available in a suitable format - for example, on the wiki. The documenter is usually the one who keeps the project tracking documentation up to date to ensure it reflects the developmental reality. While this is unlikely to be a 'full-time' role within a project team, it should be an on-going role. Part of the requirements of a successful project is providing your domain administration with updates on progress - it is almost impossible to co-ordinate a domain unless you know what everyone is doing, and how much of it they have done. A documenter can ease this burden on their domain leadership by making sure the information is available in some pre-agreed store. Developers Almost everyone on a project team will be a developer - we don't have such a fine specialisation of creator jobs on Epitaph as such that a creator will be a 'professional manager'. Everyone is expected to dual-role to a degree. Developers are responsible for actually implementing the plans that have been agreed upon, and making sure that the project leader for the project is aware of problems in a timely manner. Developer problems can revolve around many different areas, such as real-life distractions, technical issues, and social issues within the project. As long as these are being communicated to the project leader, then these blocks can be removed with appropriate redress. Real-life issues can be dealt with by reassigning some duties to a developer with more free time, technical issues can be resolved by pairing a developer with someone who has the necessary coding skills, and social issues can be resolved through mediation and conflict management. All of this can only happen if the project leader is made aware of the problems.

Subdivision of Effort and Ownership


The only way in which a project works is if everyone has their own little part of it they are developing. Part of the job of a project leader is to subdivide effort so that everyone has something to do, that they are the person who is responsible for it, and that they are happy with the work they are to do. Every task should have a named creator who is responsible for its development - if things are left as 'we're all responsible for this' it generally means that no-one is responsible. This is a well established principle in social psychological research where it goes under the name of the 'bystander effect'. Nobody works towards the task because everyone thinks someone else will do it. Responsibility in project management is another word for 'ownership' - this may seem to strike at the heart of the 'communal code' system we operate on Epitaph, so I will talk about this a little to explain why ownership is important in project development and why it doesn't violate our deeply held principles of shared source. Ownership can be expressed in a level of autonomy over design choices. A bad 474

Epitaph Online

http://drakkos.co.uk

project leader enforces a development from the top - 'This is the development you will code, and it will be coded exactly like this'. Projects that operate under this principle hardly ever succeed. In volunteer developments especially, coder motivation is a finite resource that has to be grown and nurtured, and that doesn't happen through management by fiat. Half the fun in developing is to come up with ideas and bring them to life. It's nowhere near as much fun to implement someone else's ideas. This is something that our players often don't realise - an idea report such as 'Do something cool with otters' is usually a far better prospect than a five page idea report on a deeply-complex 'otter management' subsystem - creators need to be able to take ownership for the ideas they are developing. The code that is being developed is MUD code, but delegating the responsibility for deciding what that code is going to be is a valuable motivational tool. When people have ownership over something, they have an incentive for doing well with it, and they can take a pride in its success that is not available when all you did was set out the code. Half the fun is to plan the plan. Subdividing effort goes hand in hand with delegating ownership of the project. Subdivision should impact on the 'fun stuff' as much as the 'boring stuff'. When you are subdividing effort you should make sure that some of that effort involves a degree of freedom to operate. 'You're responsible for designing and coding the quest in the castle' is a better task than 'I have designed the quest on the wiki, and it's your responsibility to code it'. The latter means that the coder can later shrug and say 'I only coded it, someone else designed it - it's nothing to do with me that it sucks'. The ownership then is not over the project or the code, but is instead over the way a part of the project is to be designed and evolved. Once the project is completed, then that ownership is lost and it transfers back to the domain (and the MUD in general). It is a short-term ownership, rather than something that is gained for the long-term.

The Epitaph Project Tracker


The MUD actually has a project management system that is available - or rather, it's a project tracker system with some management facility. You can find it http://Epitaph.atuin.net/lpc/secure/creator/project.c here. It's important to know how the project tracker works, because all projects that enter the playtesting stage must be registered with the tracker. How you choose to use it beyond that is entirely up to you. When you open up the tracker, you'll be greeted with the following menu:

475

Epitaph Online

http://drakkos.co.uk

There will also be a list of projects that have been 'touched' (modified) in the past week. Feel free to browse around these, it's good to see what's happening elsewhere in the MUD. Each domain has an entry along the top, and clicking on the link will bring up the projects that belong to this domain. Note though that this won't necessarily reflect the full range of development since projects often do not get registered with the tracker until they are in playtesting, and projects that have been abandoned often still linger because no-one has removed them. It's very simple to add a project to the tracker - you click 'add project' at the top, and you'll be given the following page:

The Project ID is the unique identifier for the project in the system. It's the special 476

Epitaph Online

http://drakkos.co.uk

code used to distinguish one project from another. This can be anything you like, but it will be made into one word when your project is added. The name is a 'human readable' short description of the project. Weve spoken about what a project leader does in a project - you should record who the leader is so there is a clear chain of authority. Notice you can only have one of these - a team can't function effectively with two chains of command. Some projects belong to several domains, and a list of these can be provided. Projects belonging to multiple domains will show up in the individual domain queries. Estimated completion is how long the project is expected to take. Note, this isn't a promise, but an estimate. You should be realistic with this - overly ambitious estimates are of no use to anyone. Next, all the creators involved with the project are listed - the links associated with a creator's name in the project tracker will bring up all of the projects with which they are involved. Project size and complexity are two important values as they directly impact on how much playtesting attention the project receives. Don't fret it too much to begin with - you can easily change any of the data you enter at a later date. The size of the project influences how many playtesters are assigned in a playtesting rotation, and how long a rotation period lasts. The complexity influences how many rotations a project receives. This is all handled automatically by the playtesting system, but setting these values correctly is important in ensuring your project gets tested effectively. A project may have several substantial subprojects, each with its own entry in the handler. You can list each of these subprojects as being part of this project. If you have a wiki for your project, then make sure you add its details. One of the benefits of the project tracker is it makes it easy to integrate all these disparate bits of information into one consistent location. Finally, you can give a short description of the project - you don't need to be too detailed about this, certainly if you have a wiki page set up. It's useful for those who want to know, at a glance, what the project involves. All projects initially start off on the 'Heap' - this is where they're officially a domain project, but no active development is ongoing. By clicking 'edit project', you can set the project to be one of a range of other states:

477

Epitaph Online

http://drakkos.co.uk

State Heap Development Playtesting

Play Limbo

Description Heap projects are on the 'todo' list of a domain. No active development is occurring. The project is active and undergoing developer attention. The project has entered the playtesting stage, and is recorded with the playtesting handlers. Don't set a project to this unless you are actually releasing it, because it triggers assignment of PTs and automated mailings. The project has been completed and is now in the game for players to enjoy (or not). The project's status is undecided, usually because some domain development has been invested but the project development team is no longer active, or because a project has left playtesting and its future state is currently under review.

When an area enters playtesting, you should also fill in the 'notes for playtesters' section indicating how the development may be tested, how it can be reached, and any features you would especially like to receive playtester attention. You can also add tasks to the project:

These tasks carry with them details on how complete they are, and this will automatically update the 'overall' completeness of the project - as you complete tasks, you'll see the project to which they belong become more complete over time. You can override this behaviour, but if your tasks are comprehensive then you won't have to.

478

Epitaph Online

http://drakkos.co.uk

Conclusion
Project management on Epitaph is about communication, not processes and diagrams. However, a basic understanding of roles, responsibilities and subdivision of effort can make an otherwise problematic project function smoothly. At the very least, having a list of your tasks and their status is hugely useful in giving you a sense of the scale of your development and progress on an incremental basis. This insight is invaluable for communicating your status to those who are responsible for co-ordinating your project across an entire domain. While the project tracker software on Epitaph is mandatory only for projects entering playtesting, it can also be used for providing easy access to your development progress to anyone who may be interested - while only your domain administration are likely to have a specific 'need to know', this visibility of effort is something that enhances the collegiality that leads to a successful development team.

479

Epitaph Online

http://drakkos.co.uk

Maintenance
Introduction
Maintenance is an on-going task in a continually evolving environment like Epitaph. I think it's fair to say that, with the exception of a few individuals, we don't do as much of it as we really should. The sheer complexity of our codebase virtually guarantees a never ending struggle to fix defects and cope with changes elsewhere in the game. It's an important process though and deserves proper consideration when we talk about how individuals in a domain should pull together to improve the quality of the code in the game and in development. Reading and understanding code requires a different set of skills to writing code they are related, but not identical, skill-sets. Maintenance involves being able to look at a piece of code and dissect its inner workings. You shouldn't turn down an opportunity to do maintenance work if it presents itself (and it always does) because you'll get a good test of skills that you may or may not yet possess, and it's a way to provide a very real positive impact on your domain.

Maintenance in the Software Development Process


We don't follow a formal software development process on Epitaph, but if we did maintenance would be at the very end of it. Maintenance is traditionally the phase of software development that consumes the largest number of resources while development is temporary, maintenance is eternal. Changes always need to be made to code, and while the problems can be multiplied by a bad software development process, they are simply a natural artifact of the complexity of software. You can't get rid of them, no matter how carefully you code and even if you could, maintenance centred on fixing problems is only part of the process. Maintenance tends to fall into one of four categories: Corrective maintenance Adaptive maintenance Perfective maintenance Preventative maintenance

Corrective maintenance is what people tend to automatically associate with the process fixing bugs. Bugs don't necessarily mean the code was badly written many problems are noticed only when enough eyeballs have been passed over the code. No matter how carefully you plan, bugs you could never have dreamed of will be reported when a development goes live. Players will always attempt things you could never have anticipated: 'Yeah, so I tried to eat the table and I got a runtime error' Adaptive maintenance is updating code to reflect changes in the underlying software systems, or the provision of additional features in existing code. In all 480

Epitaph Online

http://drakkos.co.uk

cases, this has to be done with compatibility with existing code in mind, and with provision in place for ensuring previously stored data persists over modifications. Perfective maintenance concentrates on improving the maintainability and efficiency of existing code systems. The driver on Epitaph is single-threaded, and our highly complex code-base means we eat up an awful lot of CPU and memory resources. Perfective maintenance is about improving the scalability and performance of code. Optimization is a secondary concern for sensible software developers it's only when code is operational that you can really see what code needs attention in this department. Preventative maintenance seeks to fix small problems before they become bigger problems there are always a few of those in any development. I like to refer to this as the 'we'll fix it in editing' problem every so often there is a persistent bug you can't track down, and rather than fix the root cause you apply a band-aid fix. One example of this would be if your code was persistently getting an 'off by one' error in a loop and you were unable to find where the extra iteration was coming from. You can either track it down and solve the problem (which could take hours, if not days), or say 'Oh, I'll just make it loop one less time'. That's a band-aid solution the problem isn't that the loop is being executed one extra time, the problem is that the number of loops isn't what you're expecting it to be. That's almost guaranteed to cause problems elsewhere in the code, given time. Preventative maintenance takes these band-aid problems and resolves them. The first thing that is needed for a maintenance strategy in a development environment is a formalized system for recording bugs when they are encountered. Luckily, we have a very powerful and entirely bespoke system on Epitaph for doing this. We'll get to that in a few moments.

Domain Maintenance
Few domains have a formal approach to maintenance. It tends to come and go in cycles the bug-count for a domain will reach a point whereby the domain leader says 'Fix these or I'll cut you!', everyone in the domain spends a few days reducing the bug-count to something a little less painful to look at. Then everyone goes back to their Regularly Scheduled Programming and the whole cycle repeats. Every now and again a domain appoints a 'maintenance czar', but it is often thankless and grinding work with few observable gains. No matter how hard you try, the bugs keep coming. The best approach is to have everyone pitching in that way, nobody gets overwhelmed and everyone has to take responsibility for bug-counts in their assigned directories. You'll have probably noticed that regular reports on the domain's bug-count get posted to your domain board. These are valuable updates on the relationship between bugs reported and bugs fixed. There are also useful graphs of activity to be found here showing the relationship between bugs opened, and bugs fixed. Everyone has to take ownership for this process bug fixing is a domain task, not a task for individuals. There are bug-fixing stats that can be accessed using the 481

Epitaph Online

http://drakkos.co.uk

fixed command, such as:


> fixed all 1. drakkos 2. ploosk 3. elera 4. haug 602 24 13 3

Or:
> fixed drakkos 1. drakkos 602

The tables can be a good motivational tool and a way to make a game of maintenance friendly competition within a domain can be healthy, provided it remains friendly. It's a simple rule for a domain to aim for to fix more bugs in a week than are reported that way the count heads in the right direction. It is possible, through hard-work and commitment, to get a domain bug count to single digits and keep it there. Bugs will always be reported, but if everyone takes ownership of maintenance they can be dealt with quickly and effectively that's even something players tend to remark on! Microsoft have a system of maintenance called Zero Defects (don't laugh) this doesn't mean that their code has no problems, it means that their development strategy is that no new features are added until all known issues are resolved. A variation of such a strategy would be a worthwhile policy for a domain 'no new development while the bug-count is above 50!'. This focuses everyone on the collaborative effort to manage the domain's responsibilities towards maintenance, and communicates the importance of the effort to all developers.

Where Do Bugs Come From?


Bugs come from all sources, and sometimes they are not simple to fix. Sometimes indeed they are unfixable. Sometimes they are introduced as a consequence of fixing another bug a well known informal metric is that for every two bugs you fix you run a good chance of introducing a new one as a side-effect. Another metric derives from experimental data suggests that, as far as commercial software goes, there is an average of between twenty and thirty bugs for every thousand lines of code. That's commercial software mind, written (one assumes) by people with formal training as software engineers. Sometimes bugs stem from temporary issues someone broke an important object and everyone runtimed. Those runtimes led to dozens of bug reports unrelated to the domain in which the bugs were reported, and as such don't really reflect the issues with domain code. A quick pass through the domain bug list can remove these from the statistics. 482

Epitaph Online

http://drakkos.co.uk

Sometimes they're not actually bugs at all, but ideas that have been misclassified. The more opinionated players may report their ideas as bugs on the grounds that 'My not being able to do X is a clear bug'. Our error system allows for these to be easily reclassified, so again a pass over the domain bug lists can remove these from consideration. Sometimes the bugs are legacy issues because of a mismatch between the old way of doing things and the new way of doing things, code stops working properly and nobody has re-factored the code to fit in with the new regime. Such bugs can last for a long time because the amount of effort that need be invested to fix them far outweighs any potential benefit. Sometimes the bugs are unfixable we have several of these and they are a consequence of the underlying structures not permitting certain kinds of functionality. It's hard to deny them because they are actually bugs, and you can't mark them as fixed because fixing the problem is outwith your powers. Sometimes bugs simply linger, because nobody found them interesting enough to fix. Some bugs are more interesting to deal with than others, and so these tend to be dealt with preferentially leaving the more uninteresting bugs undealt with. Every domain has a good few of these moldering in the archives..

Bug Triage
When dealing with a massive backlog of bugs, it's worth adopting some form of triage system. Triage is a system of prioritizing based on severity so that the greatest gain is obtained from limited resources. Your development time is not infinite, and time you spend bug-fixing is time that is not spent on your assigned projects. Triage can be based on impact, and also on severity. First of all, there are the critical bugs. They have to be fixed as soon as is humanly possible. Usually these are the bugs that have an impact on the functioning of the MUD as a whole infinite XP bugs, infinite cash bugs, or bugs that seriously impact on a player's ability to function (destroyed inventory, corrupted playerfiles, all that kind of stuff). All domain development should halt until these are resolved because their effect extends beyond the domain. Next come the high priority bugs that seriously impact on the functioning of code within the domain a fault in a domain-wide handler, or a fault in a domain-wide room inherit, would be examples of these. A bug that allows someone to completely reset all their faction standings would be an example of a high priority bug it doesn't impact on the MUD as a whole but seriously infringes on the correct functioning of the domain. Moreover, the scope of the bug is across the entire domain. After that are the medium priority bugs things that impact on an area of a domain but do not extend further. An issue with a key area feature would fall into this category a problem with a quest, a broken shop, or a malfunctioning area inherit would all fall into this category. 483

Epitaph Online

http://drakkos.co.uk

Finally come the low priority bugs things that affect a single room, single item, or single NPC. The priority of the bug can be cross-referenced with the severity to give you a more granular view of the issue: Issue The code is completely broken and does not load. The code gives disproportionately large advantage or disadvantage to users The code does not function with regards to its key features The code malfunctions with regards to its key features The code does not function with regards to ancillary features The code malfunctions with regards to ancillary features The code has cosmetic issues The code has typographical errors Severity Highest Highest High High Medium Medium Low Low

A malfunctioning domain level inherit that deleted player-files would thus be of high priority and highest severity the only bugs you'd look to fix before that would be critical bugs of similar severity. Having put together a triage for domain bugs, a concentrated effort can be made to resolve all the most important ones immediately, and the ones of lower priority can be addressed on a more incremental basis.

The Error Handler


Our error handler is remarkably powerful, giving considerable control over the bug-fixing process. It comes in both web and mud flavours, although the functionality is the same for both. We'll concentrate on the in-mud system here as the web system should be intuitive for anyone who understands how the process works. First of all, pick a directory that you know has bugs in it. You'll find plenty of these in the weekly domain status report that gets posted to the board. Navigate to that directory in the mud and use the command to invoke the error handler:
> errors

You'll get an interface that lets you navigate through all bugs reported on that directory. The bugs will look something like this:

484

Epitaph Online

http://drakkos.co.uk

> Bug #116252 OPEN BUG GENERAL Date Reported : Mon Jul 5 22:07:33 2010 Assigned To : drakkos Reporter : <name withheld> File name : /d/game/general Directory : /d/game I've had a few times now that I've gone to attack a zombie, or I've charged one who's trying to engage me. It says I successfully charge, and that I'm in the scrum, but nothing else then happens. I can disengage and engage and it doesn't help. I generally have to walk away. The monitor keeps ticking, though. Cheers, <name withheld> Environment: /d/game/united_kingdom/dunglen/streets/greyfriar/greyfriar18 (Greyfriar Avenue) [1 of 3] STFCOLHA-+PNGQ :

Each bug in the system has a unique ID. If you want to see that bug specifically, you can view it using the 'errors id' command:
errors id 116252

Note that there is a lot of information provided with this bug report. Along the top is the status of the bug, which can be any one of the following: Status Open Fixing Considering Fixed Denied Meaning The bug has not been dealt with, and remains an issue to be resolved. We accept this is a bug, and we are actively working to fix it. We haven't quite decided whether this is a bug or not, but we'll mark it as considering to show someone has looked at it. This was a bug and it has been fixed. This is not a bug, you are on crack.

Changing the status of a bug triggers an inform for the person who submitted the report it's not purely for our own benefit. Next is displayed the category of report it can be a bug, a typo, or an idea. Our example above is a bug of type 'room', meaning the report was submitted on the room in which the error was encountered. Bugs get categorized by which object they were reported on they can be room bugs, object bugs, help bugs, command bugs, or general bugs. This categorization allows for you to easily access the specific bugs in which you are most interested. The rest of the bug report should be fairly self explanatory, except for the gobbledygook at the bottom those are your menu options for what you can do with the error. Instructions to the handler are issued as a command code, and any associated data. Let's say for example that this bug doesn't really belong to this directory and we want to send it somewhere else. We can use 'f' (forward) along with the directory where we want it to go: 485

Epitaph Online

http://drakkos.co.uk

f /w/drakkos/

If we want to change the status, we can use 's' (status) along with the status we wish the report to have:
s fixed

If I want to provide a custom message to go along with this, I can add a 'custom' tag:
s fixed custom

Or if I don't want to send a message at at all:


s fixed none

I can use 't' to change the type of the bug:


t typo

Or 'o' to change the category:


o object

You can navigate forward and backwards with + and -. The web-based front-end works exactly the same way, except with a nicer interface and easier access to functionality:

Selecting a directory in the web handler gives you a much easier way to navigate through all the reports. A combination of the two often serves best 'errors' to get an at-a-glance look at 486

Epitaph Online

http://drakkos.co.uk

the errors in your current working directory, and the web interface for more involved work. Your mileage will vary though some people exclusively choose to use one over the other.

Conclusion
A solid maintenance strategy will help marshal individual efforts into a larger collaborative approach to domain bug-fixing. The downside of everyone owning the code is that everyone is responsible for bug-fixing quality control is a joint effort within a domain. Epitaph has some very powerful error management software, and you should make an effort to become familiar with it as it is something you'll find invaluable as you do your day to day work as a creator. While maintenance is not glamorous and does not give the same sense of player satisfaction that a new and fun piece of code will, it is vital to the well-being of the MUD. We have a massive bug count, and only by everyone pitching in will we be able to arrest the increases and eventually turn them into decreases.

487

Epitaph Online

http://drakkos.co.uk

The Experience Divide


Introduction
One of the persistent areas of conflict that exist between developers on Epitaph is the divide between 'professional' software developers, and 'amateur' programmers. I don't intend for that to be pejorative, but it's a situation that must be resolved for effective collaboration to proceed. As a matter of full disclosure, I will make mention of the fact that my degree is in software engineering, and I have been involved in teaching software development and programming at all levels of higher and further education for the past seven years. I have also been called upon for external professional consultancy and software development, as well as research in the fields of artificial intelligence, knowledge management, and accessibility support. Ive also been developing in LPC for the past ten years. Whether that makes me a professional or one of those who 'can't do, so teach' I will leave as a judgement for individual readers to reach. The fact that we require no software development experience for creators on Epitaph has been one of our strengths in the past creators require a fairly unusual set of skills in order to fulfill their day to day duties, and finding professional programmers with those skills is a daunting task. Instead, we find people with some of those skills (or who we believe can develop those skills) and work to provide an understanding of programming. In this chapter, I'm going to talk about some of the issues that come along with this, and how a constructive attitude on the part of professionals and amateurs can work to everyone's advantage.

Professional and Amateur Programmers


While many of our creators have advanced training in technical topics, comparatively few have a background in formal software development. This is a very specific topic embracing a number of esoteric disciplines across a number of broad fields requirements gathering, analysis, design and implementation. A software engineer is not the same thing as a programmer, it's a broader discipline than that. These are the professional programmers by definition, programming is their profession. On the other hand, many of our creators do have prior experience with programming in one form or another introduction to basic programming concepts is a core part of many school and university curricula. However, no matter how much experience someone may have in writing programs for themselves or as an ancillary part of their job, this development is strictly amateur. By this I don't mean that their coding is bad (their code-fu may actually be very good), I mean that the mindset that accompanies the development is not that of a professional. Partially, the difference comes down to an intended audience. Professionals, while 488

Epitaph Online

http://drakkos.co.uk

they often write programs for their own purposes, are usually writing for an audience. The intention is that, at some point in the future, real people who are not the professional will make use of the software that has been written. Amateurs tend to write for their own use to pass assessments, or to do some task that is unique to their own requirements. Amateur code may eventually make its way into the hands of others, but that's not why it was usually developed. Writing software for other people engenders a certain way of thinking about software development. An amateur programmer can compensate for a lack of formality in development when the program is being used, and can discard inconvenient requirements at will. If an amateur has written a program to check their lottery numbers, for example, they don't necessarily need to have a user interface it could be set to read the numbers in from a file. If checking for the bonus ball is too much work, then it can be left out of the functionality. The only person using the software is the person writing it. There don't need to be meaningful error messages or input validation if there's a horrible error that occurs when using the program, it's fairly easy to work out what the problem was and correct for it. Amateur development gives an opportunity for some of the programming planning stage to be shuffled to the user interaction stage where they can be ignored or compensated for. Professionals tend to fall into this mindset when writing code for themselves. I have a piece of code that I wrote for generating electronic versions of books I had written from XML documents. It has no user interface and is configured entirely with config files to anyone but me, it's unusable, and it requires the files to adhere to a fairly lax, ad hoc standard. I could never give this software to anyone else because it makes so many assumptions about what I am going to need. It's a piece of software written with an amateur mindset. Amateurs thus tend to focus on solving the problem, and the extra insight into programming that comes along with this is an extra benefit but not integral to the process. Professional development doesn't permit that luxury because you don't know who is going to be using your code (although you may have some shrewd ideas that can inform your development). There needs to be data validation, input handling, and ways to resolve ambiguity. Requirements can't simply be chopped away from a project without getting multiple people to sign off on changes. You can't simply expect people to work around bugs, or interpret misleading output. Most of all, you have to accept you are going to be rated and judged on the basis of the work you do people are actually going to see the software you put in place! Additionally, because a professional knows this is going to be the case, code is usually written in such a way to minimize the problems of future development. All the while, the professional is looking for ways in which to improve on the process so as to make future development smoother or more effective. Professionals are thus focused on the process, and the problem itself is only a stepping stone to further clarity of understanding. This is where the unique problem of Epitaph comes into play without the necessary experience in professional software development, everyone writes code the same way that an amateur does. The problem is, the code should be developed according to the mindset of a professional we're hardly ever writing 489

Epitaph Online

http://drakkos.co.uk

code for ourselves, the ultimate destination of our code is for it to be operational and experienced by players. Growing into the mindset of professional development is something all creators do eventually, if they hang around for long enough. However, the time spent in that process of developing that mindset can be a source of tension between professionals and amateurs.

Deep Smarts
More than anything else, the thing that separates an amateur developer from a professional developer is a thing known as 'deep smarts'. That doesn't mean that a professional is smarter than an amateur, it means that a lot of their insight comes from experience rather than pure natural ability, and that experience can only come with time. It takes time to build mastery in a subject usually around ten years or so before the skilled practitioner really becomes a master practitioner. Professionals are distinguished by how far they are along this process. An amateur may know more obscure coding trivia than a professional they may have more up to date skills because they are conversant with a hip technology the professional hasn't had time to become familiar with. That's not what makes someone an expert in a topic. In many ways, the building of deep smarts can be likened to a gradual internalizing of knowledge, where explicit knowledge (that which can be expressed) becomes internalized into tacit knowledge (that which cannot be expressed). People with deep smarts in their subject area can tell at a glance things that others may need to puzzle over. They understand the complexities and interactions of internal and external influences. They understand the possible alternatives and which is more appropriate for the task at hand. More than anything else, Deep Smarts is the ability to see patterns in jumbles of information. The thing about Deep Smarts is that, although some of them are transferable (social skills in one arena tend to translate quite neatly into social skills in another), on the whole mastery of one subject does not confer mastery in another. There is no short-cut to building deep smarts, you just have to wait for time and experience to develop them, and in the meantime practise the skills whenever you get an opportunity. Experts behave differently from novices they can see problems before they occur, and can make decisions swiftly, even instantly, in a way that is far beyond the capability of a novice. They can instantly identify the context of a problem or a solution because of the bank of experiences they have built up, and can make fine distinctions between one situation and another that are unknowable to a novice. They know when usual rules don't apply. Most importantly, they know what they don't know, and what they need to know in order to provide a solution to a problem. Deep smarts is the reason why the questions that a professional developer may 490

Epitaph Online

http://drakkos.co.uk

ask you about problems you are having are entirely contrary to what you may have asked in the same situation. It's why a professional can identify that the real problem with your code is on line 355 of a different object, while the MUD is telling you the problem can be found on line 48 of your own. I will reiterate that I am not saying all professional programmers have deep smarts (some do, some don't). All I am saying is that professionals, by virtue of earning a living on the basis of their software development, are further along in the process of developing deep smarts than any amateur can hope to be.

The Tension
From the perspective of professionals, a lot of the tension comes from overconfidence on the part of an amateur. There's a world of difference from writing a complicated handler to writing a simple NPC it's not just a matter of invested effort in code or understanding of the problem, it comes down to experience and ability to architect a complex solution to a complex problem. However, that's something that's obviously true only from the perspective of a professional because the amateur, by definition, doesn't have the breadth and depth of experience in building code to appreciate the gulf. Sometimes people just don't appreciate how much of a framework the MUD provides for them in terms of supporting very complex functionality with comparatively little effort. Most of these frameworks of functionality can't be relied on for lower-level MUD code. This engenders a sense of 'Well, if I could pick it up without any previous experience at all, how hard can it really be?' attitude that can spark off conflict. Accusations of 'sloppy' or 'lazy' coding can especially rankle since they almost always stem from a position of relative inexperience. Of course, such accusations are rarely constructive even when they come from a position of relative experience either, but never mind. Amateurs on the other hand have cause to resent professionals I'm sure a lot of what I have been writing comes across as patronizing (although it isn't intentional), and professionals can be unfairly dismissive of the work of others. There can also be a rather unhelpful divide between 'coders' and 'builders', with the former tending to look down on the latter. This isn't an attitude that is especially pronounced on Epitaph, but is endemic in some other MUDs. The professional who derides the work of more amateur coders is guilty of a sin far less justifiable than the lack of understanding demonstrated by an amateur. Moreover, because of the entirely different mindsets (one focused on problem solving, one focused on process improvement), collaboration between the two groups can be an exercise in frustration. The professional is well placed to give guidance, but the amateur just wants an answer to their question they don't want a lecture on process or to be told to re-engineer their approach. A lot of the advice given by professionals tends to be abstract and of dubious immediate worth it often centres on good practice, and ways of limiting problems in the future. Without the benefit of a comprehensive bank of experience, it's hard to see why this advice even matters the only way you really 491

Epitaph Online

http://drakkos.co.uk

understand the value of maintainability is when you have to completely reengineer a piece of code because someone didn't care enough about your time to write it cleanly. Part of the benefit that comes with a more professional mindset is to be able to work at a level that is appropriate for the problem this is why I mentioned in an earlier chapter that premature optimization was a bad idea... it's easily possible to over-engineer a solution when a more modest approach is warranted. Shaving half a second off of the loading time of an object that gets created when the MUD starts and stays loaded until it shuts down is not a good investment in effort. Shaving a tenth of a second off of the time taken to execute a loop in the combat handler would be a valuable efficiency improvement. Experience helps identify which situation is which. Amateur programmers especially have a tendency towards self-selection in programming tasks essentially, people pick the tasks that are most interesting to code. These may not be the things that are most valuable to code. This kind of cherry-picking of duties is good in that it ensures a developer is motivated by the work they are doing, but more problematic in that it means the less fun stuff might not end up getting the same attention. This can frustrate professionals who then feel duty bound to pick up the slack. It should also be pointed out that two professionals of different backgrounds are often a source of tension too... undoubtedly anyone familiar with the concept of 'dominance' in herds of animals can work out why! All that is required to resolve these problems is for both sides to be willing to view things from the perspective of the other, and to be a little less critical on the efforts that others have invested. That's true for everyone actually, not just those on either side of this particular divide.

Strategies for Success


There are actually great opportunities to ensure that people on both sides of this divide can work together in a way that increases the effectiveness of both, providing both sides are willing to yield a little. Pairing people together can achieve this, even if it's only an informal, ad-hoc pairing. Professionals can provide invaluable aid to amateur coders by advising on architecture of code and adherence to quality standards. A professional can also greatly increase the capabilities of a novice by delegating sections of more complicated work and requesting it be written to some set criteria (usually the parameters, return type and functionality). Getting someone to participate in a more formal process like this can really underline the importance of the exercise when they see how easily their code can slot into a larger development. The professional gets a little extra developmental effort in exchange for a little of their expertise, and the amateur gets a little more knowledge in exchange for a little effort.

492

Epitaph Online

http://drakkos.co.uk

In order for a process like this to be effective, the professional has to be willing to actually be constructive getting a piece of code back, sighing and saying 'Well, this needs rewritten' isn't going to give extra satisfaction for anyone. The professional also has to be prepared for the extra effort to be delayed gratification adding a novice to a complicated project will slow things down to begin with before it speeds them up. This kind of coaching effort can be hugely beneficial, but it has to be done in the right way. For one thing, both participants need to agree on the direction of the partnership friction on something fundamental like why something is being done will frustrate future efforts to transfer knowledge. Tacit knowledge, by its very nature, cannot be expressed. It's important then that a professional gives opportunities for an amateur to observe how something is done, as well as what is done. They should talk through their decision-making process with the amateur they won't actually be able to identify every thought that went through their head, but they might be able to signpost some of them. Knowledge building can be improved by finding some common ground between the two participants expert knowledge can be transferred effectively with small vignettes or war stories. Stories convey a lot more information than instructions do, because by their very nature they are complex and highly nuanced. A professional relating their experience through the use of relevant stories can be highly effective on two levels on one, it gives the amateur a chance to parse their own meaning from the tale, and on two it helps induct the amateur into a wider community. Simply knowing certain stories is a badge of membership in many respects there are stories about Epitaph (or more correctly, about people who are a part of Epitaph) that you simply won't know unless someone else considers you to be an insider rather than an outsider. Over fifteen years, Epitaph has had its fair share of drama and gossip, and being 'in' on that gossip reflects your inclusion in a wider social context. There is no need for a coaching process to be mandated from on high the best such collaborations form spontaneously between people who genuinely like each other. However, within a domain it is worthwhile for a more experienced developer to keep an eye on younger creators to make sure that they are well supported and producing code of which everyone can be proud.

Conclusion
I do apologise if reading this chapter made me sound like a pompous dick. My only excuse for that is that I actually am a pompous dick and it's sometimes hard to hide it. The fact that experience plays a big part in group dynamics should be uncontroversial though the important thing is making sure that the friction caused by varied levels of experience is harnessed to a worthwhile aim. Those with a more professional background in software development should do their best to respect the efforts of more amateur developers. Amateur developers should appreciate that there is a gulf of understanding between themselves and 493

Epitaph Online

http://drakkos.co.uk

professionals. If both parties do this, relative harmony can be achieved. Amateurs should take heart in the fact that they are in a tremendously good environment for learning more about software development as a process. We may not follow any sensible or formal strategies for developing our game (largely because it's hard to get volunteer developers to do anything they don't really want to do), but you'll at least understand why they are something to shoot for.

494

Epitaph Online

http://drakkos.co.uk

Wrapping Up
Introduction
So, that's our discussion of working with others, and the tools we have in place to support the process. The social aspect of Epitaph is the thing that keeps most of us logging in, day after day, week after week, and year after year. There is a genuine satisfaction that comes from working together with other people to produce something of which everyone can be proud. However, it's also possible for people to clash, fight, cause problems, and generally degrade the atmosphere for everyone. Everyone is guilty of this at one time or another, and it can be invaluable in letting people blow off a little steam in tense times. We all have to work together to make sure that the problems are not systemic.

Collegiality
I want to emphasise something I have said a number of times through this material the creatorbase is, on the whole, a collegial body. We have disagreements, sometimes very strong disagreements, but the traditions that are at the heart of being a creator on Epitaph are about openness and constructive engagement. What people misinterpret as politics are usually rooted in one or more parties not being able to integrate effectively into that environment. Collegiality implies a common respect for the commitment that everyone has to the purpose of the organization. I would hope that we all agree on that central point every creator is here to improve the game. We may disagree in how improvements are to come about, but we have to have faith that we have at least common desires in common. We are also a very informal body there is a hierarchy, to be sure, but membership of the higher ranks is based on fulfillment of a role. They are job titles, not aristocratic endowments (domain leaders may be called 'lords', but that's a title that comes from being fulfilling a particular game role). Domains are an administrative structure to ensure cohesion of purpose, not as a way to add restrictions to a body of volunteer developers. Collegiality as a founding principle of our development system requires everyone adhere to it. Fostering that is a process in which everyone has a stake.

Conclusion
That's it for now, we're done here. Could you switch off the lights on your way out? Thank you, I appreciate your kindness in these trying times. As a final note, you could really sum up the entirety of this latter section of this reading material into the aphorism proposed by our friends at Penny Arcade in 495

Epitaph Online

http://drakkos.co.uk

their range of reverential garments: Jesus says: "Don't be a dick".

496

Epitaph Online

http://drakkos.co.uk

Epitaph Quick References


497

Epitaph Online

http://drakkos.co.uk

Quick Reference - Rumour Objects


Introduction
Rumour objects are items that are designed to carry with them a narrative payload in terms of our game story - they are things like answerphones, or newspaper articles, or posters - anything that is aimed at helping players uncover, through exploration, a little more of the world around them.

Why Do We Use Them?


They provide a common mechanism that integrates easily with our rumour mill system. You don't need to worry about creating the code behind a poster or a letter - you just use the appropriate specislisation of the rumour mill code and let that do the work for you.

When to add a rumour object


You can add one of these whenever you want to provide a glimpse of the world that shouldn't come from NPC exposition or reading of descriptions.

What do they look like?


This is the code for one of the standard infection warning posters:
#include <rumour_mill.h> inherit RUMOUR_OBJECTS + "items/poster"; void setup() { set_rumour ("css barricade"); ::setup(); }

The various articulations of the rumour object framework are found in /items/rumour_objects/items/ - this one uses the framework for a poster. Most of the work that goes along with creating such an object is done via the rumour object data files - the one for this looks as follows:
::item "css barricade":: ::->type:: RUMOUR_POSTER ::->brief:: "This is a stark white poster with clear, eye-catching red letters." "It has no visual flair to it - it is purely a way to provide information " "in a way that everyone can see.\n" ::->language:: "english" ::->text:: ({ "Seek shelter in a suitable location - preferably underground, without

498

Epitaph Online windows.", "Ensure all entrances have been securely fortified.", "Secure windows and vents with plastic sheeting and duct tape.", "Switch of all unneccessary appliances.", "Listen to your radio for further instructions!" }) ::->rarity:: RUMOUR_SPECIAL ::->categories:: ({"css"})

http://drakkos.co.uk

The rumour object will interpret all of these values in t he appropriate way - for a poster, 'brief' handles the long description, and text contains the text that is readable on the object. For an answerphone, it looks like this:
::item "answer phone mess 1":: ::->type:: RUMOUR_ANSWER_PHONE ::->brief:: "A somewhat somewhat panicked voice says: " ::->language:: "english" ::->text:: ({ "Please pick up.", "Pick up, please.", "I guess you're not in - your dad's been bitten by some thugs. "at the hospital. Come quickly, please." }) ::->rarity:: RUMOUR_VERY_RARE

We're "

For this example, brief handles the starting text for messages, and each of the individual lines of text are separate chats that are given out, like so:
> A A A You press the button on the simple black answerphone. somewhat somewhat panicked voice says: Please pick up. somewhat somewhat panicked voice says: Pick up, please. somewhat somewhat panicked voice says: I guess you're not in - your dad's been bitten by some thugs. We're at the hospital. Come quickly, please.

The set_rumour function in the rumour object will permit you to link objects to rumours. If called with no parameters, it will choose a random rumour based on the rarity values set in the data file.

499

Epitaph Online

http://drakkos.co.uk

Quick Reference - Powered Items


Introduction
Powered objects are those that have external power requirements in order for them to be used at full functionality, such as laptops, mobile phones, and chainsaws. They have several extra features that distinguish them from simple, every day items.

Why Do We Use Them?


They create a scavenging requirement for players - to gain access to certain game functionality, a source of fuel must be found. The different types of fuel ensure that it is not possible to simply stockpile one single resource, and creates later trading opportunities between players.

When to add a powered item


Whenever you have an item that in the real world would drain power, it should be a powered item. The exception to this is for radios, because we want to ensure an easy social atmosphere, and that is not well-served by people losing communication because they cannot secure batteries.

What do they look like?


This is the code for the in-game flashlights:
#include <power_types.h> inherit STD_POWERED_DEVICE; void setup() { set_name ("flashlight"); set_short ("cheap flashlight"); add_adjective ("cheap"); add_alias ("torch"); set_long ("This is a cheap plastic flashlight, of the kind that you " "could imagine a child using to read under the covers after lights " "out.\n"); set_power_drain (POWER_DRAIN_VERY_LOW); set_max_power (POWER_SUPPLY_MEDIUM); set_tick_speed (POWER_DRAIN_SPEED_SLOW); set_power_type (POWER_SMALL_BATTERY); set_low_power_mess (({"The light from $I starts to flicker.\n"})); set_switch_on_mess ("$I starts to emit a pale white light.\n"); set_switch_off_mess ("$I goes dark.\n");

500

Epitaph Online

http://drakkos.co.uk

set_weight (500); } int query_light() { if (query_active()) { return 40; } return 0; } void activate_callback (object player, object room) { inform_of_light_level_change(this_object()); } void deactivate_callback (object player, object room) { inform_of_light_level_change(this_object()); }

In terms of the setup, there are only a few things that are unique to powered items. All the defines used are found in power_types.h
set_power_type (POWER_SMALL_BATTERY);

This sets the kind of power source that is used, and also handles the setting up of the proper commands on the item (so that it uses 'power' for batteries and 'fuel' for petrol.
set_max_power (POWER_SUPPLY_MEDIUM);

This sets the item's maximum power capacity. This is the amount of 'juice' it contains.
set_power_drain (POWER_DRAIN_VERY_LOW);

This sets how much power the item uses every power tick.
set_tick_speed (POWER_DRAIN_SPEED_SLOW);

This sets how often the item ticks, draining power. The other three functions in setup relate to the player view of the item - they are the messages that are received when the item is running out of power, when it is switched on, and when it is switched off:
set_low_power_mess (({"The light from $I starts to flicker.\n"})); set_switch_on_mess ("$I starts to emit a pale white light.\n"); set_switch_off_mess ("$I goes dark.\n");

When a powered item is switched on, it will return 1 for the function query_active(). This can be used to override behaviour in the item, such as is done with light in the flashlight:

501

Epitaph Online int query_light() { if (query_active()) { return 40; } return 0; }

http://drakkos.co.uk

When an item is either activated or deactivated, a callback function is called on the object, allowing you to provide whatever event-driven functionality is required. A room will not recognise when an item's light level has been changed until the function inform_of_light_level_change has been called - so we do that in the callbacks so that the new state of query_light can be used:
void activate_callback (object player, object room) { inform_of_light_level_change(this_object()); } void deactivate_callback (object player, object room) { inform_of_light_level_change(this_object()); }

Various other articulations of powered objects exist, such as weapons and armour. These work as combinations of the two inherits, such as for the chainsaw:
#include #include #include #include #include <power_types.h> <weapon_logic.h> <weapon.h> <awareness.h> <noticeability.h>

inherit STD_POWERED_WEAPON; int calc_attack( int number, int percent ) { int dam; dam = ::calc_attack (number, percent); if (query_active()) { dam *= 3; } return dam; } void setup() { set_name( "chainsaw" ); set_short("petrol powered chainsaw"); set_long("This is a scary-ass, petrol-powered chainsaw. When it's not " "running, it serves as a pretty clumsy and inefficient weapon. When " "it's running though - ye gods, you can do some damage.\n"); add_plural( "chainsaws" ); set_material (([ "steel" : 1, ])); set_weapon_attacks (WEAPON_HEAVY_SWORD, WEAPON_QUALITY_NORMAL);

502

Epitaph Online

http://drakkos.co.uk

set_switch_on_mess(one_short() + " starts up with a deafening roar.\n"); set_switch_off_mess(one_short() + " powers down with a " "'putta-putta-putta' sound.\n"); set_power_drain (POWER_DRAIN_VERY_LOW); set_max_power (POWER_SUPPLY_MEDIUM); set_tick_speed (POWER_DRAIN_SPEED_SLOW); set_power_type (POWER_PETROL); set_activate_mess ("$N pull$s a cord on $D, activating it.\n"); set_deactivate_mess ("$N pull$s a cord on $D, deactivating it.\n"); } void activate_callback() { AWARENESS_HANDLER->register_event (AWARENESS_SOUND, "roar of a chainsaw", root_environment (this_object()), -1, STRENGTH_MEDIUM, this_object()); } void deactivate_callback() { AWARENESS_HANDLER->deregister_event (AWARENESS_SOUND, "roar of a chainsaw", this_object()); } int query_noise() { if (query_active()) { return ::query_noise() + NOTICEABILITY_LEVEL_HIGH; } return ::query_noise(); } int query_breach_bonus() { if (query_active()) { return 5; } return 2; }

Note here the range of things that switching on the chainsaw does: Creates noise Provides a bonus to breaching Provides a damage bonus

Aside from the functionality that is over-ridden, it works like any other powered item, and any other weapon.

503

Epitaph Online

http://drakkos.co.uk

Quick Reference - City Street


Introduction
The way in which Epitaph handles the delivery of its city streets is through the use of procedural generation in which a basic template of a room is ornamented with landmarks. The descriptions that are used by most of the streets in the game are based on a data file which handles street-level configuration.

What is a City Street


The city streets are those that are generated from the ASCII map that drives our MTF system. If you look at the code for one of these rooms, you'll see it looks something like this:
/* Created by mtf at Fri Mar 26 17:31:28 2010 */ #include <dunglen.h> inherit CITYROOM; void setup() { set_short("Alphabet Street"); set_long("Alphabet Street continues to the east and continues to the west.\n"); set_zone("alphabet"); set_co_ord (({1300, 800, 0})); add_property("determinate", ""); set_light( 80 ); add_exit("east", ALPHABET + "alphabet07", "road"); add_exit("west", ALPHABET + "alphabet05", "road"); }

There are no descriptions or chats defined within these code files - those come from the street level config data in /data/cities/<your_city_here>/streets/

Why Do We Use Them?


City street config files let us maximise our developer output - we don't need to write each individual room - instead we write a single description for a street and allow the procedural generation elements to do the rest. This ensures our efficiency and also means that if we need to regenerate the layout of the city in the future, we don't need to make any changes to the underlying code files. We separate our infrastructure from our presentation

504

Epitaph Online

http://drakkos.co.uk

What Do They Look Like?


This is a slightly abridged data file for Alphabet Street (/data/cities/dunglen/streets/alphabet.str):
::item "alphabet1":: ::->street:: "alphabet" ::->day_long:: "Alphabet Street is a thin, cobbled road that looks as if it " "had been forgotten by urban planners for a century, an impression not " "shaken by the fact that it seems to just exist so more interesting " "buildings can have a back to them. Cobbled streets are rare in many " "cities, having been successively removed as a relic of a more archaic and " "less efficient time. Alphabet street, perhaps by virtue of the fact it's " "too narrow to allow much in the way of vehicular traffic, has been left " "with its distinctive stones. The sole feature of real interest in the " "street is the brooding mass of the Winchester bar.\n" ::->night_long:: "The cobbled stones of Alphabet Street add an extra spice " "of challenge to navigating the city at night - it's the ideal street for " "those who felt avoiding ravenous ghouls in the nighttime just didn't have " "enough of a difficulty curve for them. Luckily, it's a very thin street " "with few vectors of additional danger. Even in the darkness, the " "slumbering mass of the Winchester can be seen as a darker canvass against " "the nighttime. \n" ::->general_items:: ([ "alphabet street" : "Alphabet Street is one of those forgotten streets " "that people just don't care very much about. On the whole, it's a " "just a strip of concrete walls and back doors. It's clearly been " "permitted to keep its cobbled stones because nothing of real " "importance ever happens here.", "breathtaking aesthetic" : "Yeah, that's clearly not it. This street has " "all the old-world charm of a defecating drunk.", ]) ::->day_items:: ([ "back door" : ({"long", "Presumably the front doors are in a slighly more interesting " "location, but they may just be back doors without a front. Although " "I guess in that case they are just 'doors'", "gory", "panel"}), "the winchester" : "One of the few genuine features of the street is the " "Winchester, a large pub that serves as a social hub for the area. It " "looks like even now it's the source of some real activity." ]) ::->night_items:: ([ "concrete wall" : ({"long", "The walls are dull, uninteresting grey concrete. I'd " "like to tell you more, but there's only so much you can say about them.", "gory", "bricks"}), "the winchester" : "One of the few genuine features of the street is the " "Winchester, a large pub that serves as a social hub for the area. It " "looks like even now it's the source of some real activity. It even " "looks like there may be some light seeping out between the barricaded " "windows.", "barricaded window" : "The windows are clearly boarded up - you can tell " "that by the way the faint light is leaking through cracks in the " "barricading. Suffice to say though, there are signs of life there." ]) ::->day_chats:: ({ "The scent of stale urine assaults your nostrils.", "There is a flicker of movement over at the Winchester.", })

505

Epitaph Online

http://drakkos.co.uk

::->night_chats:: ({ "There is a flicker of light from the sleeping mass of the Winchester.", "There is a sudden sound from the Winchester, which is hushed as quickly " "as it is made.", "The scent of stale urine assaults your nostrils." }) ::->items::([ "chain-link fence" : ({ "alphabet01", "alphabet13" }), "safe zone poster" : ({ "alphabet01", "alphabet13" }), "infection poster" : ({ "alphabet14" }), "barricade poster" : ({ "alphabet07" }), ]) ::->hospital_zones:: ({ "green", }) ::->properties:: ([ NO_MAESTRO_EVENTS : 1 ]) ::->danger_level:: DANGER_LEVEL_NONE

Much of this should be self explanatory - the CITYROOM inherit handles turning all of this configuration data into an actual street room. If you're not using CITYROOM, none of this will happen. Day items, night items and general items are all provided as a mapping, which is interpreted in the city room as follows:
foreach (mixed key, mixed value in general_items) { add_item (key, value); }

As such, you can use any combination of add_item features you want within the config file. ->items refers to actual, tangible objects and where they should be cloned into rooms. The functionality for this is flexible, and based on the type of keys and values provided. For the key, we can provide either a string (which is used as the criteria for request_item), or an array (one of each will be requested). The key gets interpreted as follows: Key Type Integer String Array Description A chance, out of 100, that the item will spawn in any given room. The specific room the item will clone in The list of rooms an item will clone in

Each item, when cloned into a room, will have the callback setup_item called on it - you can use this to handle specific functionality based on where the object is placed. The chain link fences in Alphabet Street, for example, have the following code for setup_item:
void setup_item (object room) { string *ex = room->query_exits(); for (int i = 0; i < sizeof (ex); i+= 2) { room->add_obstacle (ex[i], this_object());

506

Epitaph Online } room->add_property (NO_MAESTRO_EVENTS, 1); }

http://drakkos.co.uk

The ->danger_level is used by maestro and the hospital to define how dangerous the events or NPCs that spawn in this street can be. This also sets up appropriate hospital zones, although you can also use the ->hospital_zones field to define others that should be used. You can also define a street as being an exploration node, which means that people get an XP award upon having entered it. An example of this can be found in /data/cities/dunglen/streets/scully.str:
::->exploration_nodes:: ({ "Southern Dunglen", })

Note here that exits into bespoke buildings are not handled here - those are handled via exit data files in /data/cities/<your_city>/exits/, like so in /data/cities/dunglen/exits/hospital.exi
::item "hospital":: ::->from:: ALM + "alm11" ::->to:: DUNGLEN_HOSPITAL + "main_concourse/main_reception_desk" ::->dir:: "north" ::->type:: "door"

Also note that it doesn't handle the placing of templated rooms - that is done using the build command.

507

Epitaph Online

http://drakkos.co.uk

Quick Reference - Templated Rooms


Introduction
To ensure that we have a reasonable number of indoor locations in the game, we use templated rooms - these are room files that are contextualised and placed in the game through the use of the 'build' command. Rather than hand create ten blocks of flats, instead you create a template for a block of flats and 'build' ten instances of it.

What is a Template
Templated rooms are stored in /d/game/building_templates/, and placed in the game through the 'build' command. They work exactly the same as normal buildings, except that they have special codes laced through them to ensure that the build command can orient and link them up properly.

Why Do We Use Them?


They save us a great deal of time when creating the kind of buildings that we see many times every day - houses, brand-name shops, blocks of flats, and so on. The infrastructure of the city handler means that we can rebuild these with only a command if, for example, we entirely redesign the whole of the city.

What Do They Look Like


They look like this abridged snipper from /d/game/building_templates/newsagent/front.c:
#include <dunglen.h> inherit DUNGLEN_INSIDE; void setup() { int *nums = query_seed_numbers (this_object()); int index = get_seed_element (nums, 0); index %= 2; set_short ("abandoned newsagent"); set_linked_street ("$linked$"); switch (index) { case 0: set_long ("Most newsagents in Anytown have a certain structure " "that makes them practically interchangeable. They have the same " "sad collection of newspapers, the same selection of drinks in the " "fridge, the same chocolate bars by the till. What with the " "infection and the looting that followed, they all share an " "additional feature - they all look as if they have been thorough "

508

Epitaph Online "looted. This newsagent is no exception.\n");

http://drakkos.co.uk

add_item (({"sad collection of newspaper", "sad collection"}), "Newspapers in these harsh times serve a role as valuable " "kindling, bedding, insulation and even toilet paper in a pinch. " "As such, the supply of newspapers here has been looted clean " "away."); break; case 1: set_long ("A set of yellow metal shelves dominate this room - once, " "they would have been full of interesting magazines and newspapers. "Now, they are striking in their barrenness. The fridge of the " "newsagent stands against a wall, its door wide open and its " "contents long gone. The stale smell of stagnant air fills the " "premises.\n"); add_item (({"content", "can"}), "There aren't any cans here any " "more - don't you pay attention?"); add_item ("stagnant air", "You can't see the stagnant air, but you " "can smell it. It smells... stagnant."); break; } add_survivor_graffiti(); add_exit ("$dir$", "$path$", "door"); add_exit ("$2$", __DIR__ + "backroom", "door"); }

"

There are two key features that go into a templated room - the dollar codes which are interpreted by the build command, and the randomised setup (which ensures that we can have a little internal variety between instances of the rooms). For the former, there are three of these that are important, such as in the first add_exit:
add_exit ("$dir$", "$path$", "door");

When we place a templated building, we do it with the build command - that requires us to specific a direction in which the building lies. $dir$ is replaced, when the file is written, wth the reverse of that direction. The $path$ code is replaced with the filepath of the room from which the exit was added. This line of code essentially creates your link back to wherever the building was entered. The numerical dollar codes work as a clockwise offset from the direction which was provided to the build command. If you provide 'north' as a direction, then $1$ will correspond to northeast and $2$ will correspond to east. Thus, the room will have an exit at a right angle, that right angle being defined by the orientation of the building when it is placed.
set_linked_street ("$linked$");

The $linked$ code here gets replaced with the name of the street to which the building is attached - this is used for setting up Maestro events as well as ensuring that NPCs get cloned properly through the hospital.

509

Epitaph Online

http://drakkos.co.uk

The other major element of a templated room is the random setup, which is handled like so:
int *nums = query_seed_numbers (this_object()); int index = get_seed_element (nums, 0); index %= 2;

The query_seed_numbers function will take a room object and return a set of numbers for it - these aren't random numbers, but they are as 'good as' for your purposes. They will always be the same as long as the filename of the room remains, and so you can have a 'randomised' setup without the room actually changing violently from update to update. The get_seed_element function permits you to step through the seed numbers you get, using an offset - when you reach the end of the numbers you were given, it will start from the first again. Modulous dividing (the %= symbol) a seed number by the number of possible options you have will ensure what you end up with is a number that is linked to your room, suitably random to allow for randomised setup like above, and also a number that will fall into whatever range you require. Modulous dividing, as I'm sure you recall, gives you the remainder - so it's an easy way to ensure that you don't end up with a number too big or too small.

510

Epitaph Online

http://drakkos.co.uk

Quick Reference - City Landmark


Introduction
So much of what is going to define Epitaph's descriptions is based on dynamic generation. One of the most substantial things you can add in to make the city dynamic is a city landmark, such as the Winchester or Itchycoo Park. The data files for these are quite meaty, but I'll try to document it a bit here.

What is a Landmark
A landmark is an add item that gets added to every room within range. It also comes with room chats, and additional add items. They have a relative direction which is represented in any piece of text with the code $direction$.

Why Do We Use Them?


Landmarks are part of our procedural generation system - they give us a way to add features to rooms based on proximity. That way we don't get stuck when creating city street rooms that refer to landmarks being in particular directions the MUD handles all of that for us.

When to add a landmark


Anything that would be seen from multiple places in the city should be a landmark.

Getting your Landmark to Show Up


Landmarks are loaded from the appropriate data directory in /data/cities/<city in here>/landmarks/. They're loaded when the city handler is created. If you want to load a new one, use the rehash command like so:
> rehash dunglen Directory rehashed.

It'll be a while until it propogates through all the rooms - the rooms get the various modifications done to them when they are first loaded. Update your current room to force that, but it'll take a full upload of the entire city (or at least, the bits that are impacted by the landmarks) before it looks consistent.

511

Epitaph Online

http://drakkos.co.uk

Landmarks and the City


Landmarks have four distance levels: Right here Close by Moderately close by Far away

Each of these can have their own set of short descriptions, longs, additional add items and room chats.

What does the data file look like?


Here's the data file for Itchycoo Park.
::item "itchycoo park":: ::->short:: ({ "the rolling green hills and pastoral slopes of Itchycoo Park", "the green hills and slopes of Itchycoo Park", "the green hills of Itchycoo Park", "the hills of Itchycoo Park", }) ::->alias:: ({ ({"beautiful park"}), ({"beautiful park"}), ({"pretty park"}) }) ::->descriptions:: ({ ({ "Itchycoo Park, so named for the prevalance of the park's itchycoos " "(a kind of stinging nettle) is... or at least, was... a beautiful " "park of rolling hills, beautifully tended trees and even a cool " "burn that ran through the middle. Families would go there of a " "warm day, and the sounds of conversation, laughter, and the grunts " "and shouts of recreational sports would fill the air. Now, it's just " "so much grass.", "The darkness of a city park is the closest most city dwellers, in " "pre-infection times, would get to realising just how much the night " "hates humanity. Now, Itchycoo Park is a black hole from which no " "illumination escapes. Who knows what dread terrors it may be hiding " "beneath that inpenetrable cloak of shadows.", }), ({ "Itchycoo Park, so named for the prevalance of the park's itchycoos " "(a kind of stinging nettle) is... or at least, was... a beautiful " "park of rolling hills, beautifully tended trees and even a cool " "burn that ran through the middle. Families would go there of a " "warm day, and the sounds of conversation, laughter, and the grunts " "and shouts of recreational sports would fill the air. Now, it's just " "so much grass.", "The darkness of a city park is the closest most city dwellers, in " "pre-infection times, would get to realising just how much the night " "hates humanity. Now, Itchycoo Park is a black hole from which no " "illumination escapes. Who knows what dread terrors it may be hiding " "beneath that inpenetrable cloak of shadows.", }),

512

Epitaph Online

http://drakkos.co.uk

"Itchycoo Park is, or was, a pretty park that was a popular haunt of " "families and courting couples. Now, it's just so much wasted land. "You can see the green, rolling hills off to the $direction$.", "You can't make out much about the park from here, just that it has " "hills and those hills lie off to the $direction$.",

"

}) ::->other_day_add_items:: ({ ([ "itchycoo" : "You'd need to go into the park itself to find some of " "those.", "rolling green hill" : "The hills are in the style of Scottish hills " "everywhere - no matter what direction you're walking, it's " "always uphill. Nonetheless, they have a certain harsh beauty", ({"family", "children", "courting couple"}) : "No more - those were " "happier times.", ({"sound of conversation", "laughter", "grunt"}) : "Even if you could " "see sounds, you wouldn't be able to - they're all in the past, and " "in the past they must remain.", "recreational sport" : "While the park doesn't have any actual formal " "playing fields, all you need are a few piles of coats to make an " "effective improvised alternative.", "untended grass" : "There's lots of grass, and it's all growing wild " "and untended.", "beautifully tended tree" : "The trees have become ragged and... well, " "natural. It actually might be an improvement, all told.", "cool burn" : "The burn was a small river, maybe three or four metres " "across at its broadest. Children would dangle their bare feet in " "the water on hot days.", ]), ([ "itchycoo" : "You'd need to go into the park itself to find some of " "those.", "rolling green hill" : "The hills are in the style of Scottish hills " "everywhere - no matter what direction you're walking, it's " "always uphill. Nonetheless, they have a certain harsh beauty", ({"family", "children", "courting couple"}) : "No more - those were " "happier times.", ({"sound of conversation", "laughter", "grunt"}) : "Even if you could " "see sounds, you wouldn't be able to - they're all in the past, and " "in the past they must remain.", "recreational sport" : "While the park doesn't have any actual formal " "playing fields, all you need are a few piles of coats to make an " "effective improvised alternative.", "untended grass" : "There's lots of grass, and it's all growing wild " "and untended.", "beautifully tended tree" : "The trees have become ragged and... well, " "natural. It actually might be an improvement, all told.", "cool burn" : "The burn was a small river, maybe three or four metres " "across at its broadest. Children would dangle their bare feet in " "the water on hot days.", ]), ([ ({"family", "courting couple", "popular haunt", "wasted land"}) : "It's no " "longer much of a holiday destination.", "green rolling hill" : "The hills are visible even this far away - they're " "those erratic Scottish hills that seem to defy all known geometry... " "no matter what way you walk them, it's always uphill.", ]), ([ "hill" : "That's what I said - hills.", ]), })

513

Epitaph Online

http://drakkos.co.uk

::->other_night_add_items:: ({ ([ ({"inpenetrable cloak of shadow", "darkness"}) : "It's probably no darker than " "darkness anywhere. It just... seems darker. Maybe it's psychological.", "dread terror" : "Well, you know - zombies. I guess you do know what " "dread terrors may be hiding there.", ]), ([ ({"inpenetrable cloak of shadow", "darkness"}) : "It's probably no darker than " "darkness anywhere. It just... seems darker. Maybe it's psychological.", "dread terror" : "Well, you know - zombies. I guess you do know what " "dread terrors may be hiding there.", ]), ([ ({"glow of light", "life"}) : "You couldn't possibly tell more from this " "distance, other than it looks inhabited at least.", ]), ([ ({"roof"}) : "All you can see is the roof in outline. It's very " "dark, after all.", ]), }) ::->co_ords:: ({2050,1900,0}) ::->dimensions:: LANDMARK_SIZE_LARGE ::->illuminated:: ({1, 1, 0, 0}) ::->is_are:: "are" ::->day_chats:: ({ ({ "The greenery of Itchycoo park is a melancholy reminder of happier times.", "The trees of Itchycoo Park sway a little in the wind.", "A squirrel leaps from the branches of one tree onto another.", "The grass of the park does a little dance in the air.", "The silence of the park is somewhat sinister in this city of the dead.", }), ({ "The greenery of Itchycoo park is a melancholy reminder of happier times.", "The trees of Itchycoo Park sway a little in the wind.", "A squirrel leaps from the branches of one tree onto another.", "The grass of the park does a little dance in the air.", "The silence of the park is somewhat sinister in this city of the dead.", }) }) ::->night_chats:: ({ ({ "The endless blackness of Itchycoo Park draws your eye." "Was that a groan you just heard coming from the park?" "Who knows what the cloak of darkness in the park may be hiding?" }), ({ "The endless blackness of Itchycoo Park draws your eye." "Was that a groan you just heard coming from the park?" "Who knows what the cloak of darkness in the park may be hiding?" }) })

Let's break that down bit by bit.

514

Epitaph Online

http://drakkos.co.uk

Items and Shorts


The first bit is as follows:
::item "itchycoo park":: ::->short:: ({ "the rolling green hills and pastoral slopes of Itchycoo Park", "the green hills and slopes of Itchycoo Park", "the green hills of Itchycoo Park", "the hills of Itchycoo Park", }) ::->alias:: ({ ({"beautiful park"}), ({"beautiful park"}), ({"pretty park"}) })

The item is the code for the landmark - it's how it's referenced as a key inside the handler. If you say 'Give me the landmark called 'itchycoo park', this is the landmark it'll find. The short is an array of four parts, and it breaks down into - ({ what you see when you're right by it, what you see when you're near it, what you see when you're at a medium distance, what you see when it's far away}). So if you're right by the landmark, you'll see the following:
You can see that the green hills and slopes of Itchycoo Park are close by to the east.

Aliases and Descriptions


::->alias:: ({ ({"beautiful park"}), ({"beautiful park"}), ({"pretty park"}) }) ::->descriptions:: ({ ({ "Itchycoo Park, so named for the prevalance of the park's itchycoos " "(a kind of stinging nettle) is... or at least, was... a beautiful " "park of rolling hills, beautifully tended trees and even a cool " "burn that ran through the middle. Families would go there of a " "warm day, and the sounds of conversation, laughter, and the grunts " "and shouts of recreational sports would fill the air. Now, it's just " "so much grass.", "The darkness of a city park is the closest most city dwellers, in " "pre-infection times, would get to realising just how much the night " "hates humanity. Now, Itchycoo Park is a black hole from which no " "illumination escapes. Who knows what dread terrors it may be hiding " "beneath that inpenetrable cloak of shadows.", }), ({ "Itchycoo Park, so named for the prevalance of the park's itchycoos " "(a kind of stinging nettle) is... or at least, was... a beautiful " "park of rolling hills, beautifully tended trees and even a cool " "burn that ran through the middle. Families would go there of a " "warm day, and the sounds of conversation, laughter, and the grunts "

515

Epitaph Online

http://drakkos.co.uk

"and shouts of recreational sports would fill the air. Now, it's just " "so much grass.", "The darkness of a city park is the closest most city dwellers, in " "pre-infection times, would get to realising just how much the night " "hates humanity. Now, Itchycoo Park is a black hole from which no " "illumination escapes. Who knows what dread terrors it may be hiding " "beneath that inpenetrable cloak of shadows.", }), "Itchycoo Park is, or was, a pretty park that was a popular haunt of " "families and courting couples. Now, it's just so much wasted land. " "You can see the green, rolling hills off to the $direction$.", "You can't make out much about the park from here, just that it has " "hills and those hills lie off to the $direction$.", })

Aliases are extra strings for the add item that the system will put in place. Much like with the short, these can be set for each distance level. The descriptions define the add_item for the landmark - again, an array that represents the text at the different levels of visibility. If the element at an array level is an array itself, it's the day and then the night item for the landmark.
> look beautiful park Itchycoo Park, so named for the prevalance of the park's itchycoos (a kind of stinging nettle) is... or at least, was... a beautiful park of rolling hills, beautifully tended trees and even a cool burn that ran through the middle. Families would go there of a warm day, and the sounds of conversation, laughter, and the grunts and shouts of recreational sports would fill the air. Now, it's just so much grass.

Other Add Items


Since these landmarks drive a lot of our descriptions, it's neccessary to make it so they can have additional add items associated with them. This is done through arrays of mappings - the mapping is the key and value that would be associated with the appropriate add_item:
::->other_day_add_items:: ({ ([ "itchycoo" : "You'd need to go into the park itself to find some of " "those.", "rolling green hill" : "The hills are in the style of Scottish hills " "everywhere - no matter what direction you're walking, it's " "always uphill. Nonetheless, they have a certain harsh beauty", ({"family", "children", "courting couple"}) : "No more - those were " "happier times.", ({"sound of conversation", "laughter", "grunt"}) : "Even if you could " "see sounds, you wouldn't be able to - they're all in the past, and " "in the past they must remain.", "recreational sport" : "While the park doesn't have any actual formal " "playing fields, all you need are a few piles of coats to make an " "effective improvised alternative.", "untended grass" : "There's lots of grass, and it's all growing wild " "and untended.", "beautifully tended tree" : "The trees have become ragged and... well, " "natural. It actually might be an improvement, all told.", "cool burn" : "The burn was a small river, maybe three or four metres " "across at its broadest. Children would dangle their bare feet in "

516

Epitaph Online

http://drakkos.co.uk

"the water on hot days.", ]), ([ "itchycoo" : "You'd need to go into the park itself to find some of " "those.", "rolling green hill" : "The hills are in the style of Scottish hills " "everywhere - no matter what direction you're walking, it's " "always uphill. Nonetheless, they have a certain harsh beauty", ({"family", "children", "courting couple"}) : "No more - those were " "happier times.", ({"sound of conversation", "laughter", "grunt"}) : "Even if you could " "see sounds, you wouldn't be able to - they're all in the past, and " "in the past they must remain.", "recreational sport" : "While the park doesn't have any actual formal " "playing fields, all you need are a few piles of coats to make an " "effective improvised alternative.", "untended grass" : "There's lots of grass, and it's all growing wild " "and untended.", "beautifully tended tree" : "The trees have become ragged and... well, " "natural. It actually might be an improvement, all told.", "cool burn" : "The burn was a small river, maybe three or four metres " "across at its broadest. Children would dangle their bare feet in " "the water on hot days.", ]), ([ ({"family", "courting couple", "popular haunt", "wasted land"}) : "It's no " "longer much of a holiday destination.", "green rolling hill" : "The hills are visible even this far away - they're " "those erratic Scottish hills that seem to defy all known geometry... " "no matter what way you walk them, it's always uphill.", ]), ([ "hill" : "That's what I said - hills.", ]), }) ::->other_night_add_items:: ({ ([ ({"inpenetrable cloak of shadow", "darkness"}) : "It's probably no darker than " "darkness anywhere. It just... seems darker. Maybe it's psychological.", "dread terror" : "Well, you know - zombies. I guess you do know what " "dread terrors may be hiding there.", ]), ([ ({"inpenetrable cloak of shadow", "darkness"}) : "It's probably no darker than " "darkness anywhere. It just... seems darker. Maybe it's psychological.", "dread terror" : "Well, you know - zombies. I guess you do know what " "dread terrors may be hiding there.", ]), ([ ({"glow of light", "life"}) : "You couldn't possibly tell more from this " "distance, other than it looks inhabited at least.", ]), ([ ({"roof"}) : "All you can see is the roof in outline. It's very " "dark, after all.", ]), })

Only those items at the appropriate level of visibility are added.

517

Epitaph Online

http://drakkos.co.uk

The Rest
::->co_ords:: ({2050,1900,0}) ::->dimensions:: LANDMARK_SIZE_LARGE ::->illuminated:: ({1, 1, 0, 0}) ::->is_are:: "are" ::->day_chats:: ({ ({ "The greenery of Itchycoo park is a melancholy reminder of happier times.", "The trees of Itchycoo Park sway a little in the wind.", "A squirrel leaps from the branches of one tree onto another.", "The grass of the park does a little dance in the air.", "The silence of the park is somewhat sinister in this city of the dead.", }), ({ "The greenery of Itchycoo park is a melancholy reminder of happier times.", "The trees of Itchycoo Park sway a little in the wind.", "A squirrel leaps from the branches of one tree onto another.", "The grass of the park does a little dance in the air.", "The silence of the park is somewhat sinister in this city of the dead.", }) }) ::->night_chats:: ({ ({ "The endless blackness of Itchycoo Park draws your eye." "Was that a groan you just heard coming from the park?" "Who knows what the cloak of darkness in the park may be hiding?" }), ({ "The endless blackness of Itchycoo Park draws your eye." "Was that a groan you just heard coming from the park?" "Who knows what the cloak of darkness in the park may be hiding?" }) })

Co-ords are where the landmark is actually located - all the rooms in a city have set co-ordinates, and this fixes a landmark in relation to the rest of the city. The Dimensions field is the approximate size of the landmark (which is used in calculating what is blocking other things). Illuminated is an array of numbers that indicates at particular distances whether or not the item can be seen at night. is_are determines whether or not you see the text as an 'is' or as an 'are' at particular levels - this is in relation to the short. Finally, the room chats are chats that get added to each room, once again tiered by distance.

Plotted Landmarks
The landmarking system also supports 'plotted landmarks'. Some landmarks are more subtle than, for example, 'you can see X to the Y'. Banks of a river for example are linear and not really fitted to that kind of display. For these kind of things, you can set up a plotted landmark. First, you set the plotted value to 1, and then you give a list of discrete coordinates: 518

Epitaph Online

http://drakkos.co.uk

::->plotted:: 1 ::->co_ords:: ({ ({ 200,1850,0 }), ({ 250,1800,0 }), ({ 300,1800,0 }), ({ 350,1800,0 }), ({ 400,1800,0 }), ({ 450,1800,0 }), ({ 500,1750,0 }), ({ 500,1700,0 }), ({ 500,1650,0 }), ({ 550,1600,0 }), ({ 600,1550,0 }), ({ 650,1550,0 }), ({ 700,1550,0 }), ({ 750,1550,0 }), ({ 800,1550,0 }), ({ 850,1550,0 }), ({ 900,1550,0 }), ({ 950,1550,0 }), ({ 1000,1550,0 }), ({ 1050,1550,0 }), ({ 1100,1550,0 }), ({ 1150,1550,0 }), ({ 1200,1550,0 }), ({ 1250,1550,0 }), ({ 1300,1550,0 }), ({ 1350,1500,0 }), ({ 1400,1450,0 }), ({ 1450,1450,0 }), ({ 1500,1450,0 }), ({ 1550,1450,0 }), ({ 1600,1450,0 }), ({ 1650,1500,0 }), ({ 1700,1550,0 }), ({ 1750,1550,0 }), ({ 1800,1550,0 }), ({ 1850,1550,0 }), ({ 1900,1550,0 }), ({ 1950,1550,0 }), ({ 2000,1600,0 }), ({ 2050,1600,0 }), ({ 2100,1600,0 }), ({ 2150,1600,0 }), ({ 2200,1600,0 }), ({ 2250,1600,0 }), ({ 2300,1600,0 }), ({ 2350,1600,0 }), })

These points don't describe a vector, but instead a set of co-ordinates where the landmark is visible - it's visible only at those co-ordinates and no-place else. Both the northern and southern banks of the river are handled like this, as is the river itself when you are on the bridge. It's a slightly awkward system to setup, but it permits you to have a more nuanced presentation for complex landmarks than the directional system outlined earlier in the document will allow.

519

Epitaph Online

http://drakkos.co.uk

Quick Reference - A Practise Object


Introduction
Part of the way in which we make skill increases available to players is through the use of practise objects - these are items that are scattered around the game, each of which offers a number of free skill levels, up to a certain low level, with which players can interact.

What is a Practise Object


Practise objects can be anything that can reasonably be considered as a 'proxy' for a real game system. For example, a punching bag is a reasonable proxy for unarmed combat. An easy-bake oven is a reasonable proxy for baking actual cookies. A wing-chun dummy is a reasonable proxy for martial arts.

Why Do We Use Them?


We use them to ease out the leveling curve a little and make the game a little more approachable for newbies. While we have activities aimed at each tier of skill, it can be little tedious to have to hunt down and find the exact same target in order to have a chance at a skill increase. Practise objects provide a mechanism for player trade, reducing monotony, and providing a fun and useful thing for people to find.

When to add a practise object


We add practise objects whenever we want to give someone pursuing a skill an easy leg up - every skill should have one of these, if you can come up with a reasonably interesting possible example of one.

What do they look like


Well, they look like this:
#include <task_mapper.h> inherit STD_PRACTICE; void setup() { set_name ("fence"); set_short ("$colour$ $material$ fence"); add_adjective (({})); set_long ("This is a little strip of wooden fence. It looks like it " "has been varnished many, many, many times. If you used a long, " "upwards and downwards motion on individual planks of wood in the "

520

Epitaph Online

http://drakkos.co.uk

"fence, you'd soon find that. The boards are uneven, so you'd " "probably have to do the small boards with your left hand and the " "larger boards with your right hand. Plus, use a long upwards " "and downward motion, no side to side. That's something you'd very " "much need to practice.\n"); set_weight (2000); set_salvageable (0); set_material ((["pine" : 1])); set_colour ("brown"); set_practise_task (TASKMAP_DEFENDING_BY_PARRYING); set_materials (([ "oak" : 1, ])); set_messages (({ "Up and down. Up and down. Up and down.", "No, no daniel-son. All of fence!" })); set_practise_loop (1); add_loop_command ("varnish", "the <direct:object> with a <string> stroke", ~ ({"upward", "downward"})); add_loop_command ("maintain", "the <direct:object> with your <string> hand", ({"left", "right"})); add_loop_command ("apply", "varnish to the <direct:object> with a " "long, <string> motion", ({"even", "steady", "disciplined"})); }

Most of this will be familiar from regular game objects - there are only a few special functions:
set_practise_task (TASKMAP_DEFENDING_BY_PARRYING); set_messages (({ "Up and down. Up and down. Up and down.", "No, no daniel-son. All of fence!", })); set_practise_loop (1); add_loop_command ("varnish", "the <direct:object> with a <string> stroke", ({"upward", "downward"})); add_loop_command ("maintain", "the <direct:object> with your <string> hand", ({"left", "right"}));

The practise task is the one that will be advanced each time a player practised with the item. The set_messages function handles the taskmaster informs that a player receives each time. Some items give a whack of skills upon one practise - on the whole, we don't want to do this and so we enable the object with a practise loop. That tells the MUD that we want this practise object to be one of the 'simon says' variety. The actual commands that a player must use to interact with the object are 521

Epitaph Online

http://drakkos.co.uk

handled by add_loop_command. The first parameter of this function is the command the player must use, the second is the pattern, and the third is the random string that will be used to replace <string> in the prompt. Note that the pattern here must be relatively simple, or the practise object won't know how to interpret it. A direct:object and a single string is about as complex as it can get.

Textbooks
Some practise objects are available at higher levels, and provide only one level. These are textbooks, and while they work off the same code they require slightly different setups to handle the fact they are books (or magazines, or journals, or whatever):
#include <task_mapper.h> inherit STD_TEXTBOOK; void setup() { set_name ("dummies"); set_short ("computing for dummies"); add_adjective (({"computing", "for"})); add_alias ("book"); add_plural ("books"); set_long ("This is a bright yellow book with clear, large print. It's " "aimed at complete novices who have never used a computer before - " "as a result, the material is very simplistic and doesn't go very " "far into the concepts.\n"); set_language ("english"); set_no_pages (5); set_cover_mess ("Computing for Dummies"); set_cover_chars ("neatly printed characters"); set_page_mess ("It's a series of colourful diagrams with various " "thought bubbles, insets and outsets covering the process of " "working with computers."); set_page_chars (0); set_practise_task (TASKMAP_COMPUTER_THEORY); set_object_level (1); set_messages (({ "You feel a little more assured with computer theory.", "You feel as if that book actually taught you something." })); }

Most of the code here is for setting up what people see when they interact with the book in the usual manner - set_object_level however is used to provide the range at which skills can be provided. It corresponds to the defines in task_mapper.h for task difficulty. You need to be in the approximate range of 522

Epitaph Online

http://drakkos.co.uk

TTSM_DIFF_EXTREMELY_EASY tasks, and you will be ineligible for advances if you are above this.

Object Levels
You can use object levels to set the ranges at which people should be permitted to gain free skills, and you can also set it to work 'up to' a level by using the set_full_range function:
set_full_range (1);

This means that the object will work for the set level, as well as all levels up to that level. By default, normal practise obejcts use full range, and the textbooks do not.

Powered Practise Objects


Powered practise objects will not permit someone to use them until they have been activated. One example of this is the tennis ball cannon:
#include <task_mapper.h> #include <power_types.h> inherit STD_POWERED_PRACTICE; void setup() { set_name ("cannon"); set_short ("tennis ball cannon"); add_adjective (({"tennis", "ball"})); set_long ("This hefty looking device is a tennis ball cannon - a machine " "designed for firing tennis balls at someone with the intention " "of permitting them to improve their tennis. In the grim darkness " "of the zombie apocalypse, you could probably use it to practise " "your ability to dodge furry balls flying at your face.\n"); set_weight (1000); reset_get (); set_salvageable (0); set_practise_task (TASKMAP_DEFENDING_BY_DODGING); set_switch_on_mess(one_short() + " hums into life.\n"); set_switch_off_mess("The low hum from " + one_short() + " dies away.\n"); set_power_drain (POWER_DRAIN_VERY_LOW); set_max_power (POWER_SUPPLY_VERY_HIGH); set_tick_speed (POWER_DRAIN_SPEED_SLOW); set_power_type (POWER_LARGE_BATTERY); set_activate_mess ("$N press$es a button on $D, activating it.\n"); set_deactivate_mess ("$N press$es a button on $D, deactivating it.\n"); set_messages (({ "You feel a little more confident in dodging balls to the face.", "You feel a little more able to shift your body in relation to incoming balls.", }));

523

Epitaph Online

http://drakkos.co.uk

set_practise_loop (1); add_loop_command ("dodge", "the ball from <direct:object> by moving <string>", ({"left", "right"})); add_loop_command ("duck", "the ball from <direct:object> by <string>", ({"diving", "kneeling", "lying"})); add_loop_command ("avoid", "the ball from <direct:object> by <string>", ({"jumping", "dodging", "ducking"})); }

The code for these requires the full setup for a powered object as well as for a practise object, but otherwise work identically to the non-powered variety.

524

Epitaph Online

http://drakkos.co.uk

525

Epitaph Online

http://drakkos.co.uk

Quick Reference - A Maestro Module


Introduction
Maestro is our random event generator - he sits in the background, monitoring everything that everyone does. When he gets bored, he will STRIKE, either heaping on some delicious misery, or providing a rare and welcomed element of mercy - before he heaps on some more misery.

What is a Maestro Module


Maestro is only a handler - each of the individual things that he can do to players is handled by what is known as a Maestro Module - this is a small LPC file, held in /data/maestro/ - the subdirectories in there are used to differentiate events that happen to random players, and events that occur in response to things people do (like searching).

Why Do We Use Them?


Epitaph is a horror lib, and several useful tools in building horror are suspense, tension, and surprise. Maestro makes use of things like audio and visual noticeability to ensure that there is a strategic choice for players with regards to using effective but noisy weapons and items - stay quiet, and you'll have an easier life. Make yourself noticeable, and - well, there are people watching. Maestro modules allow us to have events that are contingent on certain thresholds being met, or based on certain pre-requisites, or even just pure, undirected nastiness for the fun of it. It's a hostile world out there, and Maestro is a large part of the reason why.

When to add a Maestro Module


Whenever you have a good idea for one, really - the more o fthese we have, the more dynamic and unpredictable our game becomes. Maestro handles all the work of managing the modules - all your module needs to do is say when it should be applied, and what should happen when it is applied.

What Does A Maestro Module Look Like


Well, it looks like this (from /data/maestro/random/zombie_leg_grab.c:
#include <maestro.h> #include <noticeability.h> #include <wellbeing.h>

526

Epitaph Online #include <dunglen.h> inherit BASE_MAESTRO_INHERIT; string query_name() { return "zombie leg grab"; } int query_player_interest (class player_tracker pt) { return pt->notice[NOTICE_VISUAL] + pt->notice[NOTICE_AUDIO]; } void generate_event (object player, class player_tracker pt) { string *enemies; object room; object item; mixed *citems; string *items; object zombie;

http://drakkos.co.uk

enemies = ({"shambler.c", "child_shambler.c", "toddler_shambler.c"}); room = environment (player); item = room->query_item(); citems = item->query_cover_items(); if (!sizeof (citems)) { return; } items = ({ }); for (int i = 0; i < sizeof (citems); i += 1) { items += ({ citems[i][0] }); } zombie = create_object (DUNGLEN_CHARS_HOSPITAL + element_of (enemies)); if (!zombie) { return; } tell_object (player, zombie->a_short() + " hiding prostrate behind " + element_of (items) + " suddenly lunges out and grabs hold of your " "ankle, pulling you to the ground!\n"); tell_room (room, zombie->a_short() + " hiding prostrate behind " + element_of (items) + " suddenly lunges out and grabs hold of " + possessive (player->one_short()) + " ankle, pulling " + player->query_objective() + " to the ground!\n", ({ player })); player->set_prone (10); zombie->move (room); }

Three functions here drive the module - the first is the name, which is a unique identifier that gets used to differentiate one module from another. The query_player_interest method is the one that determines how interested Maestro should be in a player with regards to this module - every time Maestro wants to do a thing, he will check over each player, and call this function on each module for

527

Epitaph Online

http://drakkos.co.uk

each player. In this case it's very simple, it just returns the sum of visual and auditory noticeability:
int query_player_interest (class player_tracker pt) { return pt->notice[NOTICE_VISUAL] + pt->notice[NOTICE_AUDIO]; }

The class that gets passed into this function contains a pile of information about the player being considered. It's defined in maestro.h, and contains (at the time of writing), the following:
class player_tracker { string name; int health; int max_health; int energy; int max_energy; int ammo; int enemies; mapping notice; mapping interest; int interest_cache; object player; object room; }

When a player is selected by Maestro, the module to be applied is selected by roulette wheel on the basis of the values that were returned from the query_player_interest function. To get a sense of context, the numbers mean roughly this:
#define #define #define #define #define #define MAESTRO_INTEREST_NONE MAESTRO_INTEREST_VERY_LOW MAESTRO_INTEREST_LOW MAESTRO_INTEREST_MEDIUM MAESTRO_INTEREST_HIGH MAESTRO_INTEREST_VERY_HIGH 0 5 10 15 20 25

When you don't want a module to be applied to a player, it should return MAESTRO_INTEREST_NONE.

Generating the Event


Generating an event is handled by the generate_event method - when a player has been selected for Maestro attention, and when a particular module has been selected, the generate_event function is called - there's no real set pattern to this function, it's entirely contextual. The first parameter that is passed into the function is the object reference of the player that has been selected, and the second is the instance of the player_tracker class that was used previously to decide on whether or not the module should be considered for being applied to a particular player.

528

Epitaph Online

http://drakkos.co.uk

Quick Reference - Factions


Introduction
Factions are one of the most substantial things you'll end up adding as a creator a good faction has a large amount of value for a player, including making available skills, resources, trade opportunities, and missions. As such, there's quite a lot that goes into setting them up.

What is a Faction
A faction is any small to large group of people who have bonded together in a common cause. Most factions will have at least three NPCs - the mission giver, the teacher, and the shopkeeper. Factions can also provide general flavour NPCs as well as 'favour granters'.

Why Do We Use Them?


We don't have classes or guilds on Epitaph - we use Factions instead to allow for the game mechanics usually associated with these things. They also create a gameplay incentive for participation.

When to add a Faction


Any time you have a group of closely related NPCs who can be conceptually linked.

An Example Faction File


The Winchester is the first faction players encounter, and it has the following data file (located at /data/factions/winchester.fac):
::item "winchester":: ::->short:: "The Winchester Staff" ::->description:: "The Winchester is a relatively large pub on " "Alphabet Street in Anytown. As a bunch of misfits and low achievers, " "they are friendly to any survivors they encounter. " ::->initial_player_rep:: REP_LEVELS [FACTION_LIKE] ::->faction_level:: FACTION_LEVEL_MINOR ::->enemies:: ({"cat", "rat"}) ::->goods_in_demand:: ([ "foodstuff" : TRADE_INTEREST_VERY_LOW, "clothing" : TRADE_INTEREST_VERY_LOW, "weapon" : TRADE_INTEREST_NORMAL, "ranged weapon" : TRADE_INTEREST_HIGH,

529

Epitaph Online "ammo" : TRADE_INTEREST_NORMAL, "armour" : TRADE_INTEREST_NORMAL, "jewellery" : TRADE_INTEREST_VERY_LOW, "medicine" : TRADE_INTEREST_HIGH, "grenade" : TRADE_INTEREST_HIGH, "miscellaneous" : TRADE_INTEREST_VERY_LOW, "special" : TRADE_INTEREST_NORMAL, "raw resource" : TRADE_INTEREST_VERY_LOW, "processed resource" : TRADE_INTEREST_NORMAL,

http://drakkos.co.uk

]) ::->provided_favours:: ([ "handout" : FACTION_LOVE, ]) ::->handouts:: ({ "star bar", }) ::->goods_for_sale:: ([ "star bar" : 10, "bottle of water" : 10, "cheap flashlight" : 10, "small battery" : 10, "pliers" : 4 ]) ::->wanted_items:: ({"fruit", "metal"}) ::->titles:: ([ ]) ::->base_difficulty:: TTSM_DIFF_INCREDIBLY_EASY ::->npc_chats:: ([ ]) ::->minor_skills:: ({ "combat.defence.dodging", "combat.defence.parrying", "combat.melee.blunt", "combat.melee.pierce", "combat.melee.sharp", "combat.tactics.melee", }) ::->accomplishment_chats:: ([ (: $1["killed ill woman"] >= 1 :) : ({ "sayto $player_ob$ I really don't agree with you killing that woman.", "sayto $player_ob$ She might have been ill, but it was still murder.", }) ]) ::->always_encounter_actions:: ([ FACTION_LOATHE : ({ "kill $player_ob$" }), FACTION_EXALT : ({ "sayto $player_ob$ I want to have your babies." }) ]) ::->sometimes_encounter_actions:: ([ FACTION_LIKE : ({ "/nod in greeting $player_ob$"}), FACTION_LOVE : ({ "/greet $player_ob$" }), FACTION_EXALT : ({ "/greet happily $player_ob$", "' Good to see you, $player_name$!"}) ])

All of this is used to configure how the faction looks, feels, and behaves towards players. Let's look at that in smaller sections:
::item "winchester":: ::->short:: "The Winchester Staff" ::->description:: "The Winchester is a relatively large pub on " "Alphabet Street in Anytown. As a bunch of misfits and low achievers, "

530

Epitaph Online "they are friendly to any survivors they encounter. " ::->initial_player_rep:: REP_LEVELS [FACTION_LIKE] ::->faction_level:: FACTION_LEVEL_MINOR

http://drakkos.co.uk

The short and the description are used in the faction command to give some player-facing information through the factions command. Initial player rep determins how much starting reputation a player has when they encounter the faction - in this case, they start off being liked. The faction level defines how important a faction it is, and goes on to influence things such as how many skill levels they will teach.
::->enemies:: ({"cat", "rat"}) ::->goods_in_demand:: ([ "foodstuff" : TRADE_INTEREST_VERY_LOW, "clothing" : TRADE_INTEREST_VERY_LOW, "weapon" : TRADE_INTEREST_NORMAL, "ranged weapon" : TRADE_INTEREST_HIGH, "ammo" : TRADE_INTEREST_NORMAL, "armour" : TRADE_INTEREST_NORMAL, "jewellery" : TRADE_INTEREST_VERY_LOW, "medicine" : TRADE_INTEREST_HIGH, "grenade" : TRADE_INTEREST_HIGH, "miscellaneous" : TRADE_INTEREST_VERY_LOW, "special" : TRADE_INTEREST_NORMAL, "raw resource" : TRADE_INTEREST_VERY_LOW, "processed resource" : TRADE_INTEREST_NORMAL, ]) ::->provided_favours:: ([ "handout" : FACTION_LOVE, ]) ::->handouts:: ({ "star bar", })

Every faction gets free as a part of being a faction, access to the randomised mission generator. The mission generator makes use of values set in the config file to decide the random elements in the mission. Enemies is one of those values - cats and rats will be used as targets for killing missions. Goods in demand defines how much a faction will buy and sell goods for - they pay highly for things they have a high trade interest in, and less highly for things they are not as interested in. You can use these to modify supply and demand within your factions. Some factions have 'favour granting' NPCs that go along with them - the winchester has James as its favour granter, and he will make available the 'handout' favour at loved. All he will handout though is a star bar - man, way to go, James.
::->goods_for_sale:: ([ "star bar" : 10, "bottle of water" : 10, "cheap flashlight" : 10, "small battery" : 10, "pliers" : 4 ])

531

Epitaph Online

http://drakkos.co.uk

If a faction has a faction shop associated (more on this in a bit), the goods_for_sale entry defines the stock of that shop - every reset, the shop will restock itself to these values.
::->wanted_items:: ({"fruit", "metal"})

As with enemies, wanted_items is used by various mission files to generate a collection task for players - players will be asked to collect either fruit or metal for this faction.
::->minor_skills:: ({ "combat.defence.dodging", "combat.defence.parrying", "combat.melee.blunt", "combat.melee.pierce", "combat.melee.sharp", "combat.tactics.melee", })

The minor skills of a faction are those that will be taught by faction teachers - you can also set secondary and primary skills here in the same way. The level of the faction checked against the tier of skills will determine how many levels a faction will teach to particular people at particular levels. To make your faction feel more inclusive to players, you can setup some chats that get added to all faction NPCs:
::->always_encounter_actions:: ([ FACTION_LOATHE : ({ "kill $player_ob$" }), FACTION_EXALT : ({ "sayto $player_ob$ I want to have your babies." }) ]) ::->sometimes_encounter_actions:: ([ FACTION_LIKE : ({ "/nod in greeting $player_ob$"}), FACTION_LOVE : ({ "/greet $player_ob$" }), FACTION_EXALT : ({ "/greet happily $player_ob$", "' Good to see you, $player_name$!"}) ])

Always encounter actions are performed every time an NPC comes into contact with a player, and they are linked to factional standing - here, when a player is loathed, every NPC of this faction will attempt to kill them. If they are exalted, the NPCs will loudly exclaim their desire to bear the player's child. Sometimes encounter actions are the same basic thing except they won't alwas be performed - you can use them to have NPCs that occasionally do or say things in response to meeting a player. There are some other faction values, but we'll talk about them later.

Faction Infrastructure
Factions need NPCs and they need rooms - the mudlib provides several inherits and functions for these in order to simplify the task of setting up your faction. 532

Epitaph Online

http://drakkos.co.uk

First of all, to mark a room as belonging as part of a faction, you can use the add_faction function - this will mark it as 'faction safe' and this will impact on wellbeing decay for players within the confines of the area. To setup a room as being a faction shop, you inherit from STD_GENERAL_SHOP, like so:
inherit STD_GENERAL_SHOP; void setup() { set_faction ("winchester"); set_shopkeeper (DUNGLEN_CHARS_UNIQUE + "old_john"); // usual setup stuff here }

Setting the faction will setup the room to have access to the faction stockpiles (the ones that you setup earlier), and setting a shopkeeper will have it handle the cloning and setting up of the shopkeeper so as to permit access. Cities may have their own specialisations of STD_GENERAL_SHOP, so check existing examples first - Dunglen for example uses DUNGLEN_FACTION_SHOP as a base for rooms like this. For NPCs, you set one up as a trainer by having it inherit from one of the trainer articulations (such as DUNGLEN_TRAINER), and then use set_main_faction to link it to a particular faction training profile. Mission givers are handled in the same way except they extend from an articulation such as DUNGLEN_MISSION_GIVER - you will need to call set_main_faction as with trainers, but the inherits handle the rest of the code. Similarly with a favour granter - DUNGLEN_FAVOUR_GRANTER is the inherit, and you must set the main faction as above. Almost all of the code is handled for you with regards to this - all you need to do is configure it all properly. Other NPCs, ones that have no formal standing or role within a faction, can be indicated by using add_allied_faction within their setup:
add_allied_faction ("winchester", -100);

This means 'killing this NPC will result in a 100 rep penalty'. Using this hooks into the various chats and setups that go along with faction NPCs, so they will become appropriately chatty for players.

533

Epitaph Online

http://drakkos.co.uk

Quick Reference - Crafting


Introduction
The crafting system we have on Epitaph is very cool, albeit a little complex from a creator perspective as a result of its flexibility. In this document, I will explain how to setup a crafting flow from schematic to finished product.

What is crafting
Crafting is any of the workflows that take a processed resource and works it through to end up with a finished product. This is mostly based on schematics and the various craftable inherits that exist in /items/crafts/inherits.

Why Do We Use Them?


Because crafting is great, and thoroughly thematic for a game in which the means of mass production have been lost to society.

When to add a Crafting Pattern


All the time, man.

A Simple Crafting Pattern


The following is the schematic for a sewing needle, and is found in /data/schematics/sewing_needle.sch
::item "sewing needle":: ::->name:: "sewing needle" ::->description:: "This is a $needle_qual$ small $needle_mat$ sewing needle." ::->short:: "$material$ sewing needle" ::->schematic_difficulty:: TTSM_DIFF_INCREDIBLY_EASY ::->base_quality:: CRAFT_QUALITY_MEDIUM ::->schematic_tasks:: ({ }) ::->components:: ([ "needle" : "metal bar" ]); ::->finished_product:: "basic crafted implement" ::->num_products:: 5 ::->schematic_theory:: TASKMAP_MANIPULATE_METAL ::->schematic_description:: "This is the schematic for a sewing needle, for sewing." ::->component_long:: "The needle attached is made of $needle_mat$." ::->assemble_type:: ASSEMBLE_TYPE_FASHION

534

Epitaph Online

http://drakkos.co.uk

The schematic is the blueprint that permits players access to create things from other things. Most of it defines how the finished item looks and what skills are involved (although some of this is also handled by the nature of the materials going in).
::item "sewing needle":: ::->name:: "sewing needle" ::->description:: "This is a $needle_qual$ small $needle_mat$ sewing needle." ::->short:: "$material$ sewing needle"

These are the most basic setup elements - name defines the name of the schematic so we can reference it later in the game. Description is the long description that will be assigned to any item made through the system, and short is the short description. Notice here that dollar codes are used for parts of this - we'll explain about that later.
::->schematic_difficulty:: TTSM_DIFF_INCREDIBLY_EASY ::->base_quality:: CRAFT_QUALITY_MEDIUM ::->schematic_tasks:: ({ }) ::->components:: ([ "needle" : "metal bar" ]);

Each schematic has a difficulty, which is 'how hard is it to understand how to attempt this crafting'. The skill against which it is checked is handled by schematic_theory. The base_quality is the quality of the item that will come out if everything is handled 'well enough'. Character skill will alter the end quality based on the materials that go in - if you put in terrible quality metal, you'll end up with a terrible quality item at the end. Schematic_tasks permits you to add in extra checks that must be passed (with the difficulty being based on the schematic_difficulty outlined above). If you need to know a bit about explosives and a bit about science in order to make your item, you can handle that there. Components is the part of the crafting schematic that drives the process - the left hand side is a string description we use to let us link components to the long description. The right hand side is what an object must return from query_crafted_type in order to be a match for that part of the schematic. This links into the long and short descriptions so you can have separate descriptions based on what goes into the pattern - that's what the dollar codes are handling.
::->schematic_description:: "This is the schematic for a sewing needle, for sewing." ::->component_long:: "The needle attached is made of $needle_mat$." ::->assemble_type:: ASSEMBLE_TYPE_FASHION

The description is what players will see when they have that schematic in a schematic book. Assemble_type handles what kind of assemble command must be used in order to produce the item (in this case, the fashion command), and component long is a string of text that can be fed into future patterns.

535

Epitaph Online

http://drakkos.co.uk

Assuming the player manages to craft the item, they end up with something requested from the armoury, based on the finished_product set in the schematic, and the number it should produce:
::->finished_product:: "basic crafted implement" ::->num_products:: 5

This corresponds to the following code file:


#include <weapon_logic.h> #include <weapon.h> #include <crafts.h> inherit STD_CRAFTED_IMPLEMENT; void setup() { set_name ("implement"); set_short ("$material$ implement"); set_crafted_type ("$material$ implement"); set_component_long ("This is a thing, of some kind."); }

Because the long, short and component long get replaced with what is in the schematic, this ends up being five needles in the player's inventory:
This is a very low quality small silver sewing needle.

To make a schematic available, it must be placed in a schematic book. This is the code for a book of easy, simple jewellery patterns:
inherit STD_SCHEMATIC_BOOK; void setup() { set_name ("book"); set_short ("jewellery schematic book"); add_adjective (({"jewellery", "schematic"})); set_long ("This is a book of simple schematics for those looking to add " "a little bit of beauty to their harsh, punishing lives.\n"); set_no_pages (6); add_schematic add_schematic add_schematic add_schematic add_schematic add_schematic } (1, (2, (3, (4, (5, (6, "skull symbol"); "sword symbol"); "simple chain"); "simple necklace"); "simple ring"); "sewing needle");

A player must have the schematic to hand to be able to create the item, but beyond that the individual assembly commands (assemble, forge, whittle, stitch and fashion) do the work of connecting up raw components to the finished product.

536

Epitaph Online

http://drakkos.co.uk

A Slightly More Complex Example


The sewing needle takes in one raw component and spits out a very simple implement on success. Some patterns, such as those for creating necklaces:
::item "simple necklace":: ::->name:: "simple necklace" ::->description:: "$chain_long$ It is adorned with a single symbol hanging from " "the front. $symbol_long$.\n" ::->short:: "hand-made simple $material$ necklace" ::->schematic_difficulty:: TTSM_DIFF_EXTREMELY_EASY ::->base_quality:: CRAFT_QUALITY_MEDIUM ::->schematic_tasks:: ({ }) ::->components:: ([ "chain" : "jewellery chain", "symbol" : "decorative symbol", ]); ::->schematic_theory:: TASKMAP_MANIPULATE_METAL ::->finished_product:: "crafted jewellery" ::->schematic_description:: "This is the schematic for a simple necklace." ::->final_callback:: (: $1->set_type ("necklace") :) ::->assemble_type:: ASSEMBLE_TYPE_FASHION

To create this, we need both a jewellery chain (available as part of another crafting workflow) and a symbol. Here, in the long description, we use the _long code which gets replaced with the component long that is set in the schematic that created it. Notice too here there is an extra field:
::->final_callback:: (: $1->set_type ("necklace") :)

This is a function that will be called on the finished item when it is finally crafted in this case iy allows a simple generic jewellery inherit to be used for all kinds of jewellery without requiring a seperate file for each. So the simple chain:
::item "simple chain":: ::->name:: "simple chain" ::->description:: "This is a $quality$ jewellery chain made from $metal_qual$ " "$metal_mat$. It is simple and understated - just a loop of $metal_mat$.\n" ::->short:: "hand-made $material$ jewellery chain" ::->schematic_difficulty:: TTSM_DIFF_EXTREMELY_EASY ::->base_quality:: CRAFT_QUALITY_MEDIUM ::->schematic_tasks:: ({ }) ::->components:: ([ "metal" : "metal bar", ]); ::->schematic_theory:: TASKMAP_MANIPULATE_METAL ::->finished_product:: "jewellery chain" ::->component_long:: "This is a $quality$ jewellery chain made from $metal_qual$ " "$metal_mat$. It is simple and understated - just a loop of $metal_mat$.\n" ::->schematic_description:: "This is the schematic for a simple jewellery chain." ::->assemble_type:: ASSEMBLE_TYPE_FASHION

537

Epitaph Online

http://drakkos.co.uk

And a decorative symbol:


::item "sword symbol":: ::->name:: "sword symbol" ::->description:: "This is a small $metal_mat$ symbol in the shape of a sword. It looks very swashbuckly." ::->short:: "$material$ sword symbol" ::->schematic_difficulty:: TTSM_DIFF_INCREDIBLY_EASY ::->base_quality:: CRAFT_QUALITY_MEDIUM ::->schematic_tasks:: ({ }) ::->components:: ([ "metal" : "metal bar" ]); ::->finished_product:: "decorative symbol" ::->num_products:: 1 ::->schematic_theory:: TASKMAP_MANIPULATE_METAL ::->schematic_difficulty:: TTSM_DIFF_INCREDIBLY_EASY ::->schematic_description:: "This is the schematic for a sword symbol, of the kind you could " "attach as an adornment to other items." ::->component_long:: "The attached symbol is a small $metal_mat$ sword. It looks very swashbuckly." ::->assemble_type:: ASSEMBLE_TYPE_FASHION

The individual components are as straightforward as they are for a needle, but when combined we have the following interactions:
::->description:: "$chain_long$ It is adorned with a single symbol hanging " "from the front. $symbol_long$.\n" ::->components:: ([ "chain" : "jewellery chain", "symbol" : "decorative symbol", ]);

The dollar codes that can be used in the description and short of a crafted item are: Component Code $name_long$ $name_mat$ $name_qual$ Description The fully parsed component_long set in the schematic The material of that specific component The quality of the crafting of that specific component

Let's say we use a silver bar to make the chain and a gold bar to make the symbol, and we do an average job of it. We'll end up with the following for the component longs: Component Chain Component Long This is a reasonable quality jewellery chain made from reasonable quality silver. It is simple and understated - just a loop of silver. The attached symbol is a small gold sword. It looks very swashbuckly. 538

Symbol

Epitaph Online

http://drakkos.co.uk

And when we create the necklace, those component longs get used to fill out the long description of our final jewellery:
This is a reasonable quality jewellery chain made from reasonable quality silver. It is simple and understated - just a loop of silver. It is adorned with a single symbol hanging from the front. The attached symbol is a small gold sword. It looks very swashbuckly.

By allowing items to chain together like this, you can create complex craftable outputs that are uniquely linked to the materials that went in.

Finished products
Finished products, in order to have all the appropriate setup functions, must derive from one of the articulations of base objects and /mudlib/mixins/crafted_inherit, such as this one:
#include <crafts.h> #define BASIC_CRAFTED_IMPLEMENT "basic crafted implement"

inherit STD_IMPLEMENT; inherit STD_CRAFTED_INHERIT; void create() { do_setup++; implement::create(); crafted_inherit::create(); do_setup--; if (!do_setup) { this_object()->reset(); this_object()->setup(); } } mapping query_dynamic_auto_load() { mapping map = ([ ]); map = add_auto_load_value (map, BASIC_CRAFTED_IMPLEMENT, "implement", implement::query_dynamic_auto_load()); map = add_auto_load_value (map, BASIC_CRAFTED_IMPLEMENT, "crafted", crafted_inherit::query_dynamic_auto_load()); return map; } void init_dynamic_arg (mapping map, object ob) { mapping tmp; tmp = query_auto_load_value (map, BASIC_CRAFTED_IMPLEMENT, "implement"); implement::init_dynamic_arg (tmp, ob); tmp = query_auto_load_value (map, BASIC_CRAFTED_IMPLEMENT, "crafted"); crafted_inherit::init_dynamic_arg (tmp, ob); }

539

Epitaph Online

http://drakkos.co.uk

This ensures that material propogation up the crafting chain and quality modifiers are properly applied. /items/crafts/items/ contains articulations that are available for you to use, such as the crafted implement described above. A more bespoke example of a crafting articulation is the crafted machete:
#include <weapon_logic.h> #include <weapon.h> inherit STD_CRAFTED_WEAPON; void setup() { set_name ("machete"); set_short ("hand-made machete"); set_crafted_type ("machete"); } void setup_crafted_values(int quality, string desc, string sh) { ::setup_crafted_values (quality, desc, sh); set_weapon_attacks (WEAPON_SLASHING_SHORT_SWORD, quality); }

The use of this inherit allows for the quality of the weapon produced to be dependent on the success of the crafter.

540

Epitaph Online

http://drakkos.co.uk

Appendix
541

Epitaph Online

http://drakkos.co.uk

Appendix 1: Code Ownership Policy


Introduction
I'm not a lawyer of course, so this isn't all legal and watertight. The spirit of the thing should be clear though - I don't want it to be 'rules and counters', but enough for people to make informed decisions on what is and isn't okay. Note here that the intention of this page isn't to restrit anyone's rights, but instead to clarifiy what Epitpah's rights are over your code.

Code Ownership
Epitaph makes no claims on ownership of the code you write. That code is yours, for you to do with what you will. It's hope that you won't just pass copies out to anyone who asks (in order to maintain the integrity of the game), but it's your code. However, Epitaph also needs certain rights in order to function - as such, as part of being a developer on Epitaph, you grant the administration of the MUD an irrevocable right to modify, delete, and distribute that code under whatever licence is appropriate. The admninistration can also delegate responsibilities for all of those things to anyone else in the game. The code that you produce while developing on Epitaph is a donation. That specific version of the code is our version of it, for us to do with what we will. From the point of you donating the code, development is essentially 'forked' - you have your original version of the code, but the version within Epitaph is the 'Epitaph Fork' of your code. The nature of collaborative development means that after a while it is impossible to pick out who has 'moral ownership' over a file, so while the code you write is yours, it may not be appropriate for you to distribute an 'Epitaph' file. More on that in the section on shared ownership. Essentially, it's your code - if you want to take it with you to another MUD, then that's your right. But Epitaph has the right to do whatever is felt appropriate with the copy of the code that you donate.

When Does It Become Epitaph Code?


The following are our criteria for this: When it enters testing, or When it enters the game, or When it is committed to RCS in a directory outside of your /w/ directory

So, whatever you're doing in your /w/ directory - we're making no claims on that code. It only becomes ours when you transfer ownership through putting it into the live game, making it a part of ongoing testing, or committing it to RCS in a non-private directory. In cases where the code is multi-object, and parts of the system are in a live directory and parts are in a /w/ directory, then the most permissive of these criteria are used for all interacting objects - if system A is in 542

Epitaph Online

http://drakkos.co.uk

the live directories and system B is in your /w/ directory, then both parts of the code are considered 'live' and thus 'Epitaph Code'.

Epitaph and Discworld


Since many of us are current/previous Discworld coders, and since we have access to Discworld code and since we're built on a modified Discworld distribution, we have to be very careful to ensure that our dealings are above board. That means Discworld code that is not part of a public distribution and is not code that you have previously written yourself is not to be used in Epitaph without permission of the author or of the game's administration. Just because we can physically get the code doesn't mean we should. I don't think any of us are likely to think it's okay to grab code from Discworld whenever we like, but I want to make sure it's written somewhere that we don't do that. There have been MUDs in the past that have been written from Discworld codebases and made use of stolen code, and we are not one of those MUDs. In cases where such code is infringing, then the code will be deleted from Epitaph.

Shared Ownership
Code that is written by you is for you to do what you want - but one you make it available to Epitaph, that version of the code becomes the donated property of Epitaph. Code on Epitaph is owned by the MUD as a whole - it can be changed, altered, modified and changed by anyone with the delegated authority to do so. After a while, ownership of code becomes murky - when lots of people are working on a file, fixing it, altering it and adding functionality, it's hard to say that the original owner of the code still has an uncontestable claim on distribution. Rather than doing the silly thing of saying 'Well, that line of code is yours and that one isn't', we'll instead use a 'rule of thumb' system. Upon donating the file to Epitaph (as outlined above), the code remains 'your code' until the following criteria are met: You cease to be the sole contributor to RCS, and

Either: Six months of time have passed between its initial entry into RCS Five other contributors are recorded in the RCS log

So, the key criteria is that you cease being the sole contributor - if it's only you who has made changes to the code, it's uncontestably yours. However, when it ceases to be that you are the sole contributor, your right to distribute that code ends when it's been at least six months since the initial entry of the file into RCS, or when five other people have RCS contributions to the file.

543

Epitaph Online

http://drakkos.co.uk

Profiting
Epitaph is and will remain completely free to play. We are bound by both the FluffOS licence and the Discworld distribution mudlib licence, and neither of those licences permit the code to be used for commercial purposes or for any kind of monetary gain. Even if that were not the case, none of the code that you have submitted to the MUD will be used for any kind of personal profit. Epitaph however reserves the right to solicit for charitable dontations to offset support costs. Such donations will be 'strings-free' and will confer absolutely no game advantage. Such donations, should they be solicited, will be in keeping with the terms and conditions of whatever underlying driver and lib are in use at the time. Additionally, this provision extends only to that code which is written for the MUD portion of Epitaph - other merchandise (t-shirts, books, fluffy toys, whatever) may be sold at profit (provided the individuals in question have the authority to monetize Epitaph's intellectual property - check with the game's administration to make sure). In a similar vein, Epitaph branded software on other platforms (desktop applications, facebook plugins, and so on) may also be provided in exchange for money provided: None of the code is executed from the MUD Permission to monetize Epitaph's intellectual property has been provided.

The system does not hook into any system that has been donated for free to the MUD as per the code ownership policies outlined above. As some examples of the above, what would and what not be permitted (assuming right to monetize IP): A facebook app that provided an interface to the Epitaph player information centre would *not* be permitted. o It makes use of Epitaph's web system, and the processing of the player information centre is done on Epitaph. A desktop client that was tailored specifically for the Epitaph MUD *would* be permitted. A facebook app that made use of snapshots of Epitaph data for visualisation purposes *would* be permitted (provided that visualisation processing is done on another system).

Note that these restrictions apply only to profit generating systems. Facebook addons that have no charge associated with them are always permisable, although in cases where such addons put an unreasonable load on the system they may be restricted or fully blocked.

Not a Straightjacket
It's not my intention here to create a watertight contract that limits what code can 544

Epitaph Online

http://drakkos.co.uk

be distributed. If there's a file that falls out of these rules that you want to send somewhere for whatever reason, just ask and it'll most likely be fine. I just want to make sure we're all aware of what the rights are of other parties.

545

Epitaph Online

http://drakkos.co.uk

Appendix 2 - Feature Density


Note #0 by Griffin posted at Sun Apr 22 12:23:44 2001 Title: 'Feature density index (longish)' Hi, 1. Feature density: - Count the number of rooms in a street or town. Do not count inside rooms, just the "street" (or corridor) rooms. - Then count the number of rooms in that street or town that have (or are involved in) a special feature. A special feature can be a shop, a vendor, an item for a quest, a game, a post office, baths, ... any street room that is not just there for connection purposes. - Divide the number of features by the total number of rooms and you have a simple "feature density index". Counting these things is ambiguous, but it is only meant as a very rough index. 2. Examples: Ask yourself which of these places feel boring or how often you there, and then look at the feature density index: Filigree St., AM Quirm St, AM Quarry Lane, AM Attic Bee St, AM SOSG, AM Short St, AM Ave Pharaohs, KLK Hubw. Rd., KLK: Market St, KLK: Rhyme st, AM St. Cunning Artific. 9/13 = 0.7 1/13 = 0.1 5/19 = 0.3 3/11 = 0.3 7/17 = 0.4 10/24 = 0.4 8/15 = 0.5 3/18 = 0.2 2/12 = 0.2 0/8 = 0 13/19 = 0.7 26/33 = 0.8 66/140 = 0.5 6/48 = 0.2 have business

- Ohulan Cutash: - KLK - Isle of Gods, AM

These figures are rounded off and you will probably come up with slightly different results. (E.g. junctions are ambivalent, I may have missed things, you may have different ideas about what is/is not a feature.) 3. Just a rough indicator: In my evaluation, it is not a perfect match with how I feel about these places, but it is a pretty good approximation. The indicator could be improved, but then the amiguities would become bigger too I know this is not the whole story. Number of npcs, degree of detail in room descriptions, importance of the features, etc. all matter. Nevertheless, if a town or street drops below 0.3, then I would start to wonder if it will not take the overall quality of the mud down. (!!) (There are perfectly valid exceptions, of course: mazes and maze-like deserts, or a few essential connecting roads, perhaps xp farms.) 4. Empty streets and urban development I like the way Morpork was developed: if you have features, then you add a

546

Epitaph Online

http://drakkos.co.uk

street to the map and put them in. The fact that there *is* an overal map does not mean that all its streets have to be coded. This has guaranteed a lively and entertaining place with a natural, organic feel to it. This contrasts sharply with some of the more recent top-down, centralised urban planning. Why add virtually empty streets to the mud? The map makers will have fun for a few hours and then half the mud will speedwalk through them and only find them a nuisance in getting to more interesting places. Why invest a lot of cre energy in empty streets (with often tedious room descriptions as a result)? One excuse is often: "yes, but we have Plans". In recent years, several streets have been added with planned features that "would be added later". The projects then failed, but the empty streets remained. Player appreciation of such areas tends to be low and cre frustration high, with low motivation to improve an impopular area later on, since it is easier to get kudos elsewhere. Look at the indicator list again and think about how those streets and areas were developed. Again, not a perfect match, but still... 5. Prosed rules of thumb: - make an overal map, but don't start coding all the streets - put streets in *if and when* you have features for them - generally, aim for 3-7 features per 10 street rooms - add virtually empty streets only if you have a really good reason - if you have almost empty streets, then direct new features there (player-onwed housing might offer a simple fix) and ignore domain boundaries in this process. 6. Don't get me wrong: Don't get me wrong: this is not a criticism of particular streets or areas in development. I have intentionally not given examples of streets or places in pt, since these are not finished yet and it would be unfair to judge them on the basis of what they are now. Nor is this an argument against size per se. I also understand that older streets will be fuller than new ones. And I agree that not all streets have to be equally busy. And I know the risks of construction faults that can arise when you keep adding on streets. (Yes, I remember the old Mackerel St.) I just want to express a general concern as a playtester and I wonder how other playtesters feel about this. -Cheers Griffin

547

Epitaph Online

http://drakkos.co.uk

Appendix 3 - Glossary
Term driver efun lfun mudlib sfun LPC parser object inherit handler inheritance NPC update master object clone error, syntax error, warning instance Definition The operating system on which the MUD runs. It is what provides the game's efuns, as well as the networking code and the parser that interprets LPC code. An external function - a piece of code that is defined in the driver and available to all LPC objects. A local function - a piece of code that is defined inside an LPC object and thus must be executed via a local function invocation or through the call other mechanism. The LPC code that serves as the basis for the rest of the game. It consists of the base inherits, the handlers, and the various low level game servers. A simulated efun - a piece of code that is written in LPC but available to all objects in the same way as an efun. The language upon which the mudlib and the Epitaph game objects is built. The subsystem of the driver that interprets LPC code and turns it into something the computer is capable of understanding. In LPC, an object is the loaded version of a file, including its functions, its variables, and all the functions and variables that it inherits. A piece of code that is designed to be used, via the inherits keyword, as the basis of other objects in the game. A piece of code that offers a shared set of functionality between otherwise unrelated game objects. The process of an object taking on the variables and functions that are defined in another object. Non-player character - a game object that acts as a living object in the game. Updating is the process of discarding a loaded object and replacing it with a new, freshly loaded object. The object representation that is created when an object is loaded. Either the process of making a copy of an existing master object, or the name given to that copy directly. An error in the LPC code that is being used to load a game object. It's indicated by the MUD with red lines upon updating. An object with syntax errors will not load. An indication that some LPC code is either logically incorrect, or inefficient. Warnings should be fixed, but they won't stop an object from loading. Has two meanings - it is used to refer to one of a list of objects ('The malfunctioning object is this instance'), or to a set of rooms that have been cloned and made to work separately from the rest of the MUD. The process of rebuilding internal representations of data 548

rehash

Epitaph Online

http://drakkos.co.uk

files. Done through the use of the rehash command.

549

Epitaph Online

http://drakkos.co.uk

Appendix 4 - Further Reading List


Game Design
Books The Being a Better Creator material covers a lot of ground, but there is always more to talka bout than there is space to do it. I recommend each of the following books for your consideration. Name Theory of Fun Author Raph Koster Topic Perhaps the most insightful book I have ever read on the subject of game design. Don't be put off by the fact he was implicated in the awfulness that was Star Wars Galaxy. Neil Hallford, Jana An interesting look at the design Hallford decisions that go into making a roleplaying game - most valuable for its extracts from real, honest to goodness design documents, Jesse Schell This is a tremendous book on the inherent interconnectedness of good game design. Katie Salen This is a useful discussion of the implications of design in a range of contexts, from computer to board. Ed. Mark Wolf and A collection of very interesting thoughts Bernard Perron from interesting people. Lee Sheldon An insightful look at the complexities of providing genuinely interesting characterisation within the confined world of a game. Richard Bartle It's Richard Bartle! He's our field's Top Guy, and he's written a book about virtual worlds. So read it.

Swords and Circuitry

The Art of Game Design The Rules of Play The Video Game Theory Reader Character Development and Storytelling for Games Designing Virtual Worlds

550

Epitaph Online

http://drakkos.co.uk

Websites Website Sid's Rules Death of Death Why Adventure Games Suck Why Can't I Teleport Warcraft's B Team How a Board Game can Make You Cry Is Your Life Just One Big RPG Achievement Design 101 The Psychology of Video Games Figuring the Riddle of Adventure Games A Computerised Fantasy Simulation Game Players Who Suit MUDs URL http://www.designer-notes.com/?p=119 http://www.polycat.net/1780/the-death-of-death/ http://grumpygamer.com/2152210 http://www.rockpapershotgun.com/2009/06/26/w hy-cant-i-teleport-in-mmos/ http://www.eldergame.com/2009/07/the-warcraftlive-teams-b-squad/ http://www.escapistmagazine.com/articles/view/co nferences/tgc_2009/6021-TGC-2009-How-a-BoardGame-Can-Make-You-Cry http://www.g4tv.com/thefeed/blog/post/702668/D ICE-2010-Video-Design-Outside-The-Box.html http://www.gamasutra.com/blogs/GregMcClanahan /20091202/3709/Achievement_Design_101.php http://www.psychologyofgames.com/ http://www.aestheticsofplay.org/tronstad.php http://www.mud.co.uk/richard/zork.htm http://www.mud.co.uk/richard/hcds.htm

Theme and Writing


Books Writing is a big part of what gets done on Epitaph - writing well, and in theme, is the subject of these suggested reading entries. Name The Art of Writing Author Stephen King Topic This is an almost impossibly great book on the subject of writing, from one of the guys who knows how to do it best,

Websites Website Emily Short's Blog Failbetter Blog URL http://emshort.wordpress.com/ http://blog.failbettergames.com/

551

Epitaph Online

http://drakkos.co.uk

Social Contexts
While the Working with Others material will give you the necessary grounding in our tools and philosophies, there is so much more to learn. Most of it applies to development in general the fact that we are game developers does not invalidate what people have to say about constructive engagement with colleagues. The following are recommended reading for anyone who wants to delve a little more into the topic. Name The Psychology of Computer Programming Deep Smarts Bowling Alone Author Gerald Weinberg Topic A fascinating look at the psychological aspects of computer programming. This is the source of the information on egoless programming. This book covers the development of Deep Smarts, which is what characterises novices from experts Not directly related to the topics under consideration, but a full and interesting discussion of the importance of social capital. A tremendously insightful look into the power of social processes in closed systems.

Dorothy Leonard and Walter Swap Robert Putman

The Lucifer Effect

Phillip Zimbardo

Miscellaneous
Books Name My Tiny Life Play Money Author Julian Dibbel Julian Dibbel Topic This is the book that was eventually grown from the excellent Village Voice article. A fascinating look at the world of making a living from virtual assets.

552

Epitaph Online

http://drakkos.co.uk

Index
553

Epitaph Online

http://drakkos.co.uk

invisibility, 19

#
#define, 50 #include, 51 modify_exit, 53 mudlib, 34 mudlib.h, 55

A
add_exit, 44, 51 add_item, 42 add_property, 46

O B
object, 34 object, master, 35 options, creator, 28

basic template, 40 boards, bulletin, 16

P C
path.h, 50 preprocessor, 50

code, example, 33 commands, creator, 23 culture, alts, 28 culture, creator, 15, 17

R
room, 41 room, outside, 49 room_chat, 43

D
development, cycle, 29 development, incremental, 33 domains, 18 driver, 34 set_linker, 54

S T
testchar, 54 title, high lord, 19 title, lord, 19

E
error, syntax, 35 error, warning, 35

H
header, 50

U
update, 40

I
IDE, 20 inheritance, 34

554

Das könnte Ihnen auch gefallen