Multiplayer Interactive-Fiction Game-Design Blog

By Mike Rozak

 Mike@mXac.com.au,

Or Mike_Rozak@yahoo.com.au

Copyleft 2003 – 2013.

Free redistribution allowed.

 

 


 

Version 1, 26 October 2012

Version 2, 3 November 2012

Version 3, 10 November 2012

Version 4, 16 December 2012

Version 5, 7 March 2013

Version 6, 14 March 2013

Version 7, 8 June 2013

 

 

 

Click to Quick-jump

CircumReality (Multiplayer Interactive Fiction) Game-design walk-through. 12

Running the world-server on your own computer. 12

Running the CircumReality game client 15

Logged into the game server, before you begin playing. 20

On another planet 24

“Gary” cut-scene. 31

“Gary” Cut-scene code. 34

Screenshots of the tools. 41

M3D.exe. 41

CircumRealityWorldSim.exe. 46

M3DWave.exe. 47

MNLP.EXE. 48

Circumreality’s Game-play. 49

To Be Continued... 54

Multiplayer Interactive-fiction Computer-Game Design. 55

About 56

Asheron's Call 2 critique. 57

Evolutionary explanation for entertainment 70

The trouble with explorers. 77

Story and plot vs. freedom in virtual reality. 130

Why text adventures aren't commercially viable. 144

Virtual worlds and virtual holidays. 156

The attraction of impossibility. 162

Virtual world spectrum... 166

Virtual world as platform... 177

The really big VW problems. 183

Stop the buffet 198

Junk Food Entertainment 208

Steady-state approximation. 215

Choice. 227

The anti-MMORPG.. 233

The end.. 245

Build it and they will come. 257

Intertwined storylines. 273

Player vs. X.. 276

Choice, part 2. 286

Small VW operators vs. large. 295

Automatically generated content 303

The authoring equation. 308

The toy room... 315

The player pyramid.. 328

Digging their grave. 343

Altruism... 350

Differentiation. 361

Intertwined relationships. 366

Text vs. graphics. 377

Fun Factors. 382

Player Powers. 386

Virtual world equation. 393

Theme in virtual worlds. 403

Experience points. 410

The game with a thousand faces. 415

The parlour, the lobby, and the sand box. 425

Why traditional MUDs/MMORPGs work. 436

The fun in genres. 442

Game classification. 451

We don't always get what we want 453

The iron law of reality. 459

The ecology of a MMORPG.. 465

Of mice and elephants. 471

Problem solving.. 476

Exceptional physics. 483

The dating game. 491

Choice and consequences. 499

Fractured reality. 506

Sympathetic goals. 509

Cost cutting.. 516

Sub-games with variation. 520

The law of new inventions. 523

My current "grand unified theory" of avatar games. 526

The four pillars. 532

A tangle. 537

The peacock. 539

Analysis of Peter Molyneux's Fable. 545

More thoughts on the strengths of text 558

Avoiding elves, orcs, and hobbits. 560

When I was a child.. 569

Personal NPCs. 571

Story arcs and quests. 582

Storylines II 588

The game loop.. 598

Dream factory. 601

(A theory of massively single-player games) 608

Puzzles and problem solving.. 621

Quest design 101. 629

Choices III 638

Word of mouth. 643

Levels, friends, and lobbies. 652

Oblivion: Full spectrum content, from hand-generated to procedural 662

Emotions. 673

Gates and keys. 679

The NPC-conversation wall 682

My current grand-unified theory of multiplayer avatar games. 689

The hero's journey... kind of. 694

Un-designing Oblivion. 704

Stickybeaking.. 718

Neverwinter Nights 2 Analysis. 730

IF Title Design 101. 737

Quests, stories, and spaghetti 742

Programmed intuition. 750

Interactive fiction equation. 756

Interactive fiction vs. games (and world-like worlds) 768

Interactive fiction equation 2. 773

Personal virtual worlds. 789

Topographies. 796

Multiplayer interactive fiction. 800

Nutritional game design. 816

Making players forget they're playing a game. 827

A rose-filled world.. 832

Immersion-emotion feedback loop. 840

Choices, part 4. 844

Why allow players choice?. 850

Fallout analysis (the recipe) 854

Posted on a game-designer’s private web-forum.. 865

And a follow-up Post: 865

Text-to-speech papers. 867

Blizzard challenge 2007. 868

Blizzard Challenge 2008. 884

Introduction. 884

Blizzard challenge 2008 results compared to 2007. 885

Failure analysis. 887

Acoustic model 887

Prosody model 888

Changes between Blizzard 2007 and Blizzard 2008. 889

General changes. 889

Acoustic model 890

Unit selection. 890

Prosody model 890

Prosody synthesis. 890

Acoustic synthesis. 890

Objectively calculating target and join costs. 891

F0 target cost 891

F0 target costs with PCM... 892

Energy target costs. 892

Duration target costs. 893

Start/end word target costs. 893

Mismatched left/right context target costs. 893

Non-contiguous join costs estimates. 895

Join costs. 896

USTC/iFlytek. 896

Conclusions and future work. 896

References. 897

Blizzard Challenge 2009. 898

Introduction. 898

Blizzard Challenge 2009 results compared to 2008. 899

A talking speech recognizer. 900

Conclusions and future work. 902

References. 904

Source-code. 905

Multiplayer Interactive-Fiction Scripting-Code. 908

Overview.. 908

Documentation. 908

Documentation – Standard Library. 908

Documentation – Server Library. 997

Documentation – Basic interactive Fiction Library. 1020

Documentation – Basic Administration Library. 1136

Documentation – Basic communications Library. 1151

Documentation – Basic RPG Library. 1152

Documentation – Basic Artificial Intelligence Library. 1240

Documentation – Basic fantasy Library. 1381

Documentation – Basic Monsters Library. 1388

Documentation – Extra Objects. 1388

Documentation – Sample world. 1388

Library – Standard Library. 1390

Overview.. 1390

List of Strings. 1391

List of Resources. 1391

Method Definitions. 1391

Property Definitions. 1454

Objects. 1454

Functions. 1454

Variables (Global) 1496

Library – Server Library. 1497

Overview.. 1497

List of Strings. 1497

List of Resources. 1498

Method Definitions. 1499

Property Definitions. 1506

Objects. 1509

Functions. 1519

Variables (Global) 1606

Library – Basic interactive Fiction Library. 1630

Library – Basic Administration Library. 1630

Library – Basic communications Library. 1630

Library – Basic RPG Library. 1630

Library – Basic Artificial Intelligence Library. 1631

Library – Basic fantasy Library. 1631

Library – Basic Monsters Library. 1631

Library – Extra Objects. 1631

Library – Sample world. 1631

 

 

 


 

CircumReality (Multiplayer Interactive Fiction) Game-design walk-through

 

This is a walkthrough of my CircumReality (Multiplayer interactive fiction) game. The source-code is available on http://www.CircumReality.com/mXacSourceCode.zip. (See the “Source code” section in this document.)

A piece-wise download of the .zip file is available at:

1.       http://www.CircumReality.com/mXacSourceCode_part1.zip

2.       http://www.CircumReality.com/mXacSourceCode_part2.zip

3.       http://www.CircumReality.com/mXacSourceCode_part3.zip

4.       http://www.CircumReality.com/mXacSourceCode_part4.zip

5.       http://www.CircumReality.com/mXacSourceCode_part5.zip

6.       http://www.CircumReality.com/mXacSourceCode_part6.zip

7.       http://www.CircumReality.com/mXacSourceCode_part7.zip

8.       http://www.CircumReality.com/mXacSourceCode_part8.zip

9.       http://www.CircumReality.com/mXacSourceCode_part9.zip

 

Running the world-server on your own computer

After installing the CircumReality game (from www.CircumReality.com), you can play CircumReality directly from my game-server. If it is still running.

If my game-server is no-longer running, then you can still play CircumReality:

1.       In the “c:\program files\mXac\CircumReality” directory:

 

2.       Run “CircumRealityWorldSim.exe”.

 

3.       If you have not already unzipped the mXacSourceCode.zip file, then do so now.

 

4.       Someplace in the .zip file, you will find a file named, “AshtariEmpire.crf”. As of this writing, the file is, 38,225KB.

 

AshtariEmpire.crf may ONLY be available in the piece-wise mXacSourceCode_partX.zip files, where X is 1..plus.

 

5.       Copy “AshtariEmpire.crf” to its own directory, such as “c:\temp”.

 

6.       In the World Simulator, press the “Dialog” button near the filename.

 

7.       In the open-file dialog-box, type in “c:\temp\AshtariEmpire.crf”, and press the “Open” button.

 

8.       Back in the World Simulator, press “Open (and run)”

 

 

The window that appears shows all of the users connected to your CircumReality game – which is available only on your computer... and (kind of) if you happen to tell your friends what your computer’s IP address is.

 

Running the CircumReality game client

1.       In the “c:\program files\mXac\CircumRealty” directory, run the “Circumreality.exe” application.

 

2.       The second time that you run CircumReality.exe, a pester-window appears requesting payment for the game. The game still works without paying, although some graphics-functionality and text-to-speech functionality is limited. Payment is NOT supported at the moment, since the game is unfinished.

 

 

Skip through to “Play the Trial version”.

 

3.       You might (or might-not) be prompted with another HTML-like page (using my “Escarpment.dll” code, source-code included in mXacSourceCode.zip). This page prompts you for your user name.

 


Every person in the household would have a different user-name. Each user-name has an associated password-file. The password-file lets players play-in multiple CircumReality worlds, just like text-MUD players can use a MUD client to play in many different MUD-worlds. (
http://en.wikipedia.org/wiki/MUD)

 

If this window shows up, press “Create a new password file”.

 

 

Type in a user-name, such as “Test Now”.

 

Click “Next”.

 

 

A preferences screen, with preferences common to all CircumReality worlds appears.

 

Leave it unmodified, and press “Next” again.

 

When I pressed “Next”, somehow (I don’t remember programming it), my CircumReality game automatically logged onto my local server.

 

4.       If you are NOT automatically logged onto the local server by the CircumReality client, then....

 

5.       In the “User password files” page, you will find your username, “Test Now” listed.

 

6.       Click on “Test Now”.

 

7.       You may see the following page:

 

 

8.       Click on “Try playing in a new world”.

 

9.       You will see the following dialog-page:

 

 

10.    Under the “File name” edit box, type “c:\temp”, and press enter.

 

11.    The page-display changes to show the worlds (.crf files) in the local directory:

 

 

12.    Click on “AshtariEmpire.crf” in the list-box.

 

13.    Press the “Play” button.

 

14.    A randomly-generated user-name and password are automatically saved in your password file.

 

Logged into the game server, before you begin playing

1.       The following screen will appear in CircumReality.exe:

 

 

2.       If you look on the “World Simulator” window, you will see that you are logged on:

 

 

3.       Back in the game-client, scroll-through the “Rules of conduct” and press the “I agree to abide by these conditions” button.

 

CircumReality hosts its own HTML-like windows (provided through Escarpment.dll, in the mxacSourceCode.zip download). HTML-like pages WITHIN the game are critical for user-interface design (and flexibility)!

 

4.       Select a race:

 

 

5.       If you select “Show a (fe)male portrait”, the dialog-“tab” switches to “Chat” (I’ll show that later), and random portrait is displayed.

 

 

ALL of this is handled by the HTML-like displays for the windows, as well as the multi-core RENDERER built-into CircumReality.

 

6.       Select a gender. (If the player has entered preferences to always use the same gender, then they are NOT asked this question.)

 

 

7.       Type in the character’s name. If the player has selected a name that they always use, then the player will-not have to type in a name.

 

 

8.       Since text-to-speech is used EVERYWHERE in the game, you should press “Speak my name” to hear what your chosen-name sounds like when it is mispronounced by text-to-speech.

 

9.       Press “OK”.

 

On another planet

1.       You begin the game on a different planet, from the main game-world. The game was supposed to end on the “different” planet. Since I only finished 20% of the content, the game does-not end... oh well.

 

 

2.       The first thing that you might wish to do is increase (or decrease) the image-quality. There is an option at the top. You can also turn-off text-to-speech.

 

A higher image quality:

 

 

3.       You look-around the room by clicking-and-dragging on the room image.

 

4.       Your character defaults to a random face. To change your character’s face, click on your character’s image, underneath the room display:

 

 

5.       Every “object” has a menu of 2-to-6 options, as well as a more-advanced, “What else can I do?” Click on “What else can I do?”

 

 

6.       Oops! The appearance-changing option isn’t listed. I may have coded the character-image so that “Change my appearance” does-not appear in the menu until later.

 

7.       In the edit box, type in the natural-language command, “Change my appearance”. (REMEMBER: The program is not-yet finished. The user-interface is INCOMPLETE and ROUGH.)

 

8.       The following UI appears in the lower-right corner of the screen:

 

 

9.       The most-fun way to change your character’s appearance is to press “Press this” link, to show a number of different images.

 

 

10.    Click on an image you like.

 

 

11.    Click on the “Keep this face” option.

 

12.    Repeat until you find the best face for your character.

 

13.    NOTE: I ONLY show the character’s head, since (a) it’s a lot-less work to 3-D model, and (b) this provides for better emotes.

 

14.    Click on the “Book” image in the lower-left corner of the screen.

 

 

 

15.    Move your mouse over the enlarged book-image, and click on the enlarged book-image. The cursor will show “Open <Object>”.

 

16.    The book opens, but due to a user-interface bug, you only see the text as very-small, and unreadable. If you are actually playing-along, you will-have noticed a text-to-speech voice speaking-out narration such as “You open “My adventures in Amroth.””

 

 

17.    To view the book enlarged, move your mouse-cursor to the top of the screen. An icon-menu will slide down; it is mostly filled with emote icons. Click on the “Zoom” tab:

 

 

18.    The book is kind-of like a linking-book in the Myst series. It is also a journal, showing-you how you have affected the world, like the radio in Bethesda Game Studio’s Fallout 3. (THE RADIO FEATURE in Fallout is VERY-VERY COOL/IMPORTANT.) (http://en.wikipedia.org/wiki/Fallout_3, http://en.wikipedia.org/wiki/Myst_(series) )

 

19.    You can have the book read to you by clicking on the text, where the mouse-pointer tooltip shows, “Read the right page of <Object>”.

 

20.    To enter the world, press the “Enter the story” menu-option, visible in the menu on your right.

 

 

“Gary” cut-scene

1.       You teleport (linking-book-style) from the planet-room to a row-boat, being rowed to a small island. (Due-to a bug, the background screen doesn’t change properly.)

 

 

2.       After a short cut-scene, you are provided with a few choices:

 

 

3.       You can ask Gary some questions, shown on the right. Try that.

 

4.       Then press, “Please continue rowing”.

 

5.       More cut-scene play.

 

6.       Press “Please continue rowing” again.

 

7.       Your character eventually arrives at the pier.

 

 

 

8.       Click “Climb onto the pier.”

 

 

9.       You now are in a room, with a 360-degree view of the pier. You can rotate around and look in different directions. The images on the lower-right section of the screen shows you players, non-player characters (NPCs), and objects in the room.

 

10.    As you stand around, Gary enacts an “idle” activity that is narrated. “Gary pulls a piece of grass, and twiddles it between his fingers.” Etcetera. All NPCs potentially have idle narrations.

 

11.    Sunset from the pier.

 

 

 

“Gary” Cut-scene code

Each segment of the cut-scene is a MIFL (mXac Interactive Fiction Library) object. (Code is available in the mXacSourceCode.zip file download.)

 

 

The rCutSceneEvansworthApproach1 resource looks like this:

 

 

Screenshots of the tools

M3D.exe

M3D.exe is a 3D editor, used to create 3D models and 3D worlds used in CircumReality. (The source-code is included in the mXacSourceCode.zip download.)

 

If/when you get this start-up error, either (a) find the code in the source-code and remove the time/date check, or (b) temporarily set your computer-clock back.

Below is the Stibbles.m3d file, located in the mXacSourceCode.zip download.

 

 

Stibbles.m3d from a different angle:

 

Creating a new building user-interface:

 

Adding windows to the building user-interface:

 

Character heads:

 

Painting a surface user-interface:

 

The terrain editor:

 

 

CircumRealityWorldSim.exe

This tool is an integrated-development environment for editing a CircumReality world. It also “runs” the world on a remote server, or the player’s computer. (The source-code is included in the mXacSourceCode.zip download.)

 

 

M3DWave.exe

Wave-editor. (The source-code is included in the mXacSourceCode.zip download.)

 

 

MNLP.EXE

This tool lets you create your own text-to-speech voice. (The source-code is included in the mXacSourceCode.zip download.)

 

 

Circumreality’s Game-play

1.       Choice-fiction (“Choose your own adventure” and “Fighting Fantasy”) game-play is intermingled with other game-play. (http://en.wikipedia.org/wiki/Choose_Your_Own_Adventure, http://en.wikipedia.org/wiki/Fighting_fantasy)

 

2.       Use Cyan’s Myst-like 360-degree surround-images for graphics. Also like Riven, and Myst III:Exile. (http://en.wikipedia.org/wiki/Myst, http://en.wikipedia.org/wiki/Riven, http://en.wikipedia.org/wiki/Myst_III:_Exile)

 

3.       Add Zork-like interactive fiction. (http://en.wikipedia.org/wiki/Zork, http://en.wikipedia.org/wiki/Interactive_fiction)

 

4.       Text-MUD-like features are added. MUD = Multiuser dungeon. (http://en.wikipedia.org/wiki/MUD)

 

a.        Players select a character-race and gender.

 

b.       Add text-MUD-like combat. Combat is NOT the core of game-play, though.

 

c.        Player-characters can learn-and-improve skills, computer-role-playing-game like. “Skills” have turned-out to be unimportant to CircumReality game-play though.  (http://en.wikipedia.org/wiki/Role-playing_video_game)

 

d.       Add multiplayer capabilities from text-MUDs. CircumReality also supports single-player no-internet play. And, “run your own private game for a few hours on the weekend,” so that you can play with a small group of friends.

 

e.       Players can team-together into “parties”.

 

f.         Players can undertake player-versus-player activities, NPC social-manipulation as well as duels.

 

g.        Game-play is script-driven. New game-play can be added, such as card-games, real-estate, etcetera. As per LP-MUD. (http://en.wikipedia.org/wiki/LPMud)

 

h.       Non-player characters (NPCs) act as item-and-quest vending machines.

 

5.       Add non-player characters (NPCs), with capabilities similar to Bethesda Game Studio’s Oblivion, Fallout 3, and Skyrim. (http://en.wikipedia.org/wiki/The_Elder_Scrolls_IV:_Oblivion, http://en.wikipedia.org/wiki/Fallout_3, http://en.wikipedia.org/wiki/The_Elder_Scrolls_V:_Skyrim)

 

a.        The NPCs have daily schedules that they maintain. Working, sleeping, eating, meeting friends, etcetera. They also have goals.

 

b.       Players can listen-in on conversations between NPCs.

 

c.        Players can complete “quests” for NPCs.

 

d.       NPCs act as equipment vendors.

 

e.       NPCs are sources of information (and stories).

 

f.         Players can “talk to” NPCs by selecting an item from a short-menu of questions/responses.

 

6.       As players change the world... Similar to Fallout 3. (http://en.wikipedia.org/wiki/Fallout_3)

 

a.        NPCs disappear, or move-in.

 

b.       The player’s accomplishments are written into a journal, along with descriptions of the effects of their accomplishments. Somewhat like a village newspaper. This feature is similar to Fallout 13’s radio-announcer.

 

7.       NPCs speak using text-to-speech (and transplanted prosody). (http://en.wikipedia.org/wiki/Speech_synthesis)

 

8.       Players can “talk to” NPCs by typing in natural-language questions, such as “Where is the cafe?”, “Is Fred friends with anyone?”, “How much is my sword worth?”, and “What time is it?”

 

a.        Chatter-bot functionality is also possible. (http://en.wikipedia.org/wiki/Chatterbot)

 

9.       NPCs have relationships with other NPCs:

 

a.        When an NPC likes/trusts a player-character more, the NPC’s friends like/trust the player-character more. The NPC’s enemies dislike/mistrust the player-character more.

 

b.       NPCs are in “factions”, similar to extended relationships.

 

c.        Players (and player-characters) can infer NPC relationships by watching NPCs interact. Or, players can ask the NPCs (with natural-language processing) about their relationships – which the NPCs sometimes lie-about or deny. Or, players can ask OTHER NPCs about relationships between NPCs.

 

d.       Players have a “journal” that displays graphs of the NPC relationships.

 

10.    Players can get NPCs to like/trust them by:

 

a.        When players complete “quests”, NPCs like/trust the player’s character more.

 

b.       Gifting objects (and money) to the NPCs, such as boxes of chocolates. Specific NPCs only accept certain gifts. Players can determine what gifts NPCs like by observing the NPCs, or by asking other NPCs what the NPC likes.

 

c.        Being polite to the NPCs.

 

d.       NPCs occasionally ask the player-characters personality-test questions. If the player answers the questions correctly, the NPCs will like/trust the player better. “So what do you think about my new dress?” Players are provided with 3-to-4 possible answers. As NPCs like/trust the player-character more, it becomes more-important for players to answer the question correctly, as expected by the NPC.

 

e.       Players can tell the NPCs rumours. (See below.)

 

f.         NPCs observe player-character’s actions, and modify their like/trust of the player-character. One NPC in the game notices if the player helps cleans rooms for him.

 

g.        Befriending the NPC’s friend improves a NPC’s like/trust. Becoming an enemy of the NPC’s enemy has a reverse effect.

 

11.    Players can “find” and propagate rumours.

 

a.        When players complete quests, NPCs sometimes tell the players rumours.

 

b.       When a NPC likes/trusts a player enough, the NPC sometimes tells the player a rumour.

 

c.        When players observe NPCs interacting, their player-character sometimes learns a rumour.

 

d.       Player-characters can learn rumours from notes, diaries, NPC-spoken stories, etcetera.

 

e.       Players can pass the rumours amongst themselves.

 

f.         Players have a journal with a list of rumours, and which NPCs the rumours are associated with.

 

 

 

To Be Continued...

BUGBUG – I am still working  on this

 


 

 

Multiplayer Interactive-fiction Computer-Game Design

 

 

About

This section is a collection of web-page “blogs” that I wrote about computer-game design, specifically for multiplayer interactive-fiction and massively-multiplayer online gaming.

Many/most of the web-page links are broken.

 


 

 

Asheron's Call 2 critique

(Back to TOC)

19 August 2003

by Mike Rozak

A few months ago I purchased my first MMORPG, Asheron's Call 2. Sadly, I was underwhelmed. It didn't deliver the experience I was expecting. I tried Anarchy Online, but it was even less interesting. After reading many other MMORPG reviews, I have concluded that none (yet) deliver on MMORPG's true potential.

Rather than sulk, I decided to write this document to critique the flaws in AC2, as a representative of MMORPGs in general. This critique gets a bit negative. I'm not trying to rip into AC2, but point out areas for improvement. AC2 does lots of things right, but I won't bother to mention them here since this isn't a magazine review trying to provide an unbiased feel for AC2. It's more of a post-mortem, a technical term by software companies when they review what went right and what went wrong in a project.

I am E-mailing this document to a handful of MMORPG companies, although I suspect most will ignore it.

I'm not in the target market

I wish to point out at the beginning that I don't really fit the target market for AC2:

Now that I've clarified my point-of-view, let me proceed with AC2's analysis:

Boiling it all down

When I approach a problem I like to look at in different ways so I can get a better grip on the entire problem. One technique for looking at a problem is to boil it down to its essence. It's the "What are we trying to accomplish here?" question. For a MMORPG, my answer is this:

MMORPGs are a themed virtual playground for adults (and teenagers). Instead of swings, merry-go-rounds, and slides, the adults are given combat, economics, empire building, etc. A playground is also an environment that encourages socialisation; so too with a MMORPG.

I'll touch on this more later, but if you think about MMORPG as a playground you'll see that many of their features are actually contrary to a MMORPG's goal.

User interface

I have been designing user interfaces for years, so I think I'm good at it. Some people would disagree. My user interfaces tend to create opinions; people either really like them or really hate them.

The first problem with AC2 user interface is clutter; it has many different floating windows (map, character stats, inventory, radar, help, etc.) that not only obscure the scene, but are drawn with cool-looking half-transparent backgrounds that make reading the small 9-point type difficult to read. I have two suggestions:

1.         If there's a second monitor, put all the clutter on the second monitor and use the primary monitor for just the 3D image. Most people don't have a second monitor, so this idea really doesn't work.

2.         Display the 3D image in 16:9 widescreen at the top of the 4:3 screen. Put all the clutter on the bottom of the screen in a divided window. Perhaps even allow users to control how large the divided area is.

AC2 should use text-to-speech... Sure, text-to-speech sounds awful. It's a lot better than trying to simultaneously read 9-point type and fight a monster though. (I am a bit biased since I used to work in the speech technology group at Microsoft.)

Remove the radar window. I discovered that I used the radar window a lot. This is actually a problem since it shifted the focus of the game from being a first person experience to a submarine battle, and ultimately took some of the fun out of the game. The purpose of the radar window is so that the small field-of-view from the 3D view doesn't become a frustrating hindrance to game play. One alternative to the radar might be to display two 3D views, one with a 60 degree field of view that occupies most of the screen. The other would have a 180 degree field of view that would allow for peripheral vision without the detail; the 180 degree FOV image could be immediately below the 60 degree FOV image, and one third the height.

The maps could have been better. AC2 only provides a global map (with no detail) and a zoomed in map with more detail, but not enough. Multiple zooms and more detail would greatly improved things. Plus, not having all the points of interest clearly marked would made exploration more fun.

The in-game help was poor. The web-site help is much better, but because AC2 can't handle an Alt-Tab to a web browser I can't both play AC2 and use the internet help. Besides, the in-game help should be a mirror of the HTML version.

Socialisation

Given that MMORPGs are online and have thousands of players, one would assume that they would encourage these thousands of players to interact and entertain one another. This is what merry-go-rounds do, since the more people on a merry-go-round the more fun it is.

AC2 doesn't seem to go far enough to encourage socialisation, for a few reasons:

Customising avatars

People like to have characters that look different from the other players. AC2 does not facilitate this:

The world

AC2's world is huge; I suspect many MMORPG designers assume that bigger worlds are better. I thought this too, until I played AC2:

Combat

One of the toys that players in AC2 have is combat. AC2's combat, however, is not fun. In previous games where I found combat fun, it has been fun because of three reasons:

Ultimately, combat in AC2 becomes make-work, something you have to do in order to reach the next level. You try to reach the next level a) because it's there, and b) because you can't explore further without becoming stronger. Given that exploration in AC2 isn't too exciting, the purpose of combat is only to increase your level for the sake of increasing your level.

I have a limited attention span and was bored with this repetitive pattern by level 5. I did stay with AC2 to level 16 in desperate hope that it would eventually get interesting, and because I didn't have anything else to play.

Some other annoying aspects of combat are:

Lack of puzzles

While I like the idea of quests in AC2, I was disappointed that the quests were ultimately scavenger hunts for one or more items. The obstacles between your avatar and the item were lots of beasties to kill. That's it.

A few puzzles scattered here and there would have made the quests more interesting. I suspect that AC2 didn't include puzzles because their solutions would be posted on the Internet about 30 seconds after the first person solved the puzzle. Sure, some people would cheat. But most wouldn't. After all, cheats are available for all adventure games. (Confession: I do use cheats, but only when I've been bashing my head against a puzzle for hours.)

Lack of NPCs

Many MMORPGs discard NPCs (non-player characters) because they think that with thousands of real people playing there's not need for NPCs.

This concept extends to shopping: Goods and services are expected to be player-to-player interactions, not purchases from a NPC at a shop.

I don't think the player-run economy model works...

1.         When I ran an adventure BBS in 1986 I wrote in a item selling scheme like E-bay. I thought it was cool; I was creating my own microeconomic. Well, prices never seemed to stabilise. This could be because I didn't have enough players. An E-Bay like service might work with 10,000 players, but AC2 doesn't even supply the feature.

2.         After playing AC2, I did some reading about Ultimate Online's history. It seems they've had lots of problems with their virtual economy, either with inflation or people hoarding all the money.

My suggestion would be:

Item crafting

Many MMORPGs include an item crafting feature. This allows characters to make equipment for their own use or to sell. Item crafting is included because a) it gives players something to do and provides easy-to-create quests, and b) creates an economy that should (theoretically) make the game more fun.

While I agree with this in theory, it didn't work well for me in AC2 because:

Miscellaneous

Asheron's Call 2 appeared in Australian computer stores many months after the US release. I assumed this was because they were setting up servers in Australia or SE Asia. They obviously weren't, because my only choice of server was in North America.

My game experience would have been better if they did have more local servers:

Other directions

As I stated earlier, MMORPG are really virtual playgrounds for adults. AC2 provides the following "toys" for its players: combat, exploration, and item creation. Other toys could be added: (Many of these ideas are half-baked and shouldn't be taken too seriously.)

 


 

Evolutionary explanation for entertainment

(Back to TOC)

8 November 2003

by Mike Rozak

For awhile I've been thinking about the question: "What makes games fun to play?", or more broadly, "What is entertaining?". Needless to say, you can come up with a list of thousands of entries, none of which really answer the question.

Frustrated by the huge number of answers, I took a different approach by defining entertainment: Entertainment is an activity that keeps people interested in itself despite the fact that there are no obvious economic rewards (aka: work). So what is entertaining? This list was just as bad as the "fun" list.

I then tried to tackle a simpler problem. Having spent a lot of time around animals (I have volunteered at a few zoos), I decided to answer a slightly different question, "What keeps an animal's interest?", or more specifically, "What keeps a primate's interest?"

This one is a bit easier to answer:

Animals also have some internal "drives" (or instincts) that encourage their actions:

If you look at the above activities you'll notice that they do a pretty good job of keeping your interest too. Interestingly, all of the above are fairly common entertainment devices. Some forms of entertainment exploit them better than others; books rarely use the "food" drive because words just don't compare to the real thing, but danger and socialisation are common themes in novels.

Humans are different than other primates though, so I'll include a few other items that interest humans alone:

So what does this prove? Not much, yet.

I have listed a number of external stimuli and internal drives that will keep you and/or an animal interested... at least for a while. If an animal (or you) get too much of any particular stimulation it will get bored (so to speak) and go onto something else. Boredom acts as a safety switch to ensure that an animal doesn't become obsessed with the activity, since obsession often leads to death and/or failure to breed. In humans the failure for boredom to kick in is considered a mental disorder. (Alcoholism, food addiction, computer nerd, etc.)

While an animal can have too much of a stimulus, it can also have too little. Instincts dictate that an animal which doesn't get enough food will seek out food. The same obviously applies to narcotics, sex, socialisation, ferreting, hunting and gathering, and migration. I suspect (although cannot prove) that if an animal doesn't have enough danger or "new"-ness it will also seek these out. Humans obviously do (danger = adrenaline activities, new-ness = take up a new hobby, etc.)

To summarise my theory:

1.         Animals' (and humans') interest can be captured through various external stimuli and internal drives. These stimuli/drives can be categorised into a relatively short list.

2.         If an animal (or human) gets too much of a stimuli/drive, it will become bored and ignore it.

3.         If the animal (or human) doesn't get enough, it will seek it out.

Fine. But how does this relate to entertainment?

Modern society is a recent invention. Throughout most of homo sapien's (that's us) evolution, we were sitting in a savanna in Africa hunting for our food and being chased by lions. Our genetics are not attuned to modern life; they are attuned to life 1 million years ago.

This may be too much to swallow for you, especially if you you're a descendent of Adam and Eve. Let me give you an example on a less controversial animal, a house cat, which is designed to hunt in the wild. If you lock it up in a house, the cat will display some odd behaviours, namely chasing pieces of string around. That's because it doesn't have any prey to chase, so its hunting drive needs some outlet. I suspect that if the cat were able to satiate its hunting drive in the wild, it wouldn't be nearly as interested in a tasteless and easy-to-catch piece of string.

The same goes for a human. Modern society provides us with plenty of food and usually (but not always) socialisation. There aren't many lions chasing us around though. And we don't get much of a chance for hunting and gathering (although a shopping mall trip comes fairly close to gathering). My theory predicts that people will seek out whatever need is un-met. (Specifics will vary from person to person since not only will their daily experiences differ, but so will their genetics.) Entertainment is how we do this.

Following this logic, people that like to jump out of airplanes are obviously exposed to less danger in real life than their genes would recommend. Those interested in soap operas aren't getting enough socialisation (gossip) in their life. People that go on holiday are fulfilling the migration and/or exploration urge. (They're also trying to escape from the stress of their every-day lives.) Etc.

But how does this relate to fun?

When you ask someone why they participate in an entertainment, they usually say, "Because it's fun." From this I make the cognitive leap that our sense of fun is a codeword for "Because it's entertaining," or "Because it keeps my interest - and I'm not even getting paid for it." (Some people will play a game even when it's no longer fun, but that's become they've become obsessed with winning at it.)

Putting my marketing hat on... Even if "fun" is not the same as "keeping one's interest" it doesn't matter. As long as it keeps the user's interest more than any other activity it will sell, so it's just as good as fun.

I suspect most people reading this are thinking, "Interesting, but way too simplistic. It doesn't explain why I like to do X." True, it's simplistic, and true it can't be used to explain everything, but (from my perspective at least) it provides a basis for explaining why an activity is fun. This is infinitely more useful than no basis at all.

If this theory is true, what are the consequences?

1.         All good entertainments have elements of danger, socialisation, exploration, etc. That's why it's common to go out to dinner (socialisation and food) and then a movie (danger and exploration). Conversely, if you have a dinner party at home (socialisation and food) you always try a new exotic recipe (danger and exploration). Okay, it's not possible to have all the elements included in an entertainment, but most good entertainments fulfil a variety of drives.

2.         If you know what a person is missing in their life you can invent an entertainment for them. (This isn't a new idea and doesn't have much of an effect because people are self selecting; those that need socialisation will tend towards a social game, etc.)

3.         Corollary: A computer game can model the user's need for danger, socialisation, etc. based upon a questionnaire or some other method. From this it can determine how much danger, etc. it should introduce into the game, and even guess when the user's danger-drive has been satisfied (aka: the user is getting bored) and a new drive (such as exploration) can be emphasised. (In writing terms this control of danger, exploration, socialisation, etc. is known as the "tension" of a story arc.) This of course, is difficult to do.


There's one thing I forgot to mention: Why do I think that stories in themselves are interesting to humans?

My thinking falls along the following lines:

Humans have been able to speak for several hundred thousand years, maybe more. They have been sitting around campfires and telling stories for just as long.

At first the elders were just passing on common wisdom to younger tribe members without the story, such as "Don't eat red berries because you'll die." As anyone knows, being told something is not the same actually seeing it or experience it yourself. Being told that red berries are poisonous is not as "sticky" as actually eating a red berry and getting very sick (or even dying) or seeing a friend eat the berry and get sick or die.

So, elders would spruce up their words of wisdom by attaching them to reality. "Your Uncle Ug ate red berries and died." This helped because it also included the socialisation instinct/drive into the equation, making the message just a bit more sticky.

A generation later though, socialisation didn't come into the equation because no one alive knew Uncle Ug any more. Connecting him to the message made no difference so it was back to square one. The solution was to give Uncle Ug relevance by first describing him as a person and as part of the clan. Only then do you kill him off by making him eat the berries. A story started to form.

The story was further extended with more words of wisdom. After all, an elder isn't going to spend 10 minutes describing Uncle Ug (so he becomes part of the clan), and then only include only one sentence of wisdom about him... "Uncle Ug was a great any mighty warrior... blah blah blah... he was my father's great uncle. blah blah blah... One day he ate a red berry and died. The end."

The elder sticks some other bits of wisdom in, such as "White berries are good to eat" and "To hunt a tiger you do X, Y, and Z." All of these pearls are strung together with some more narrative about Uncle Ug first hunting the tiger and doing X, Y, and Z, and then eating the white berries. He was still hungry so eat ate a red berry and keeled over. Using this trick, the elder kills thee birds with one stone.

However, for some people this still wasn't sticky enough because they didn't buy the long explanation of why Uncle Ug was important. (History teachers still have the same problem today.)

Genetics solved this problem though. Those people that didn't believe the story about Uncle Ug didn't pay too much attention to not eating the red berries, and well, they eventually ate one and died. Those that paid attention lived. The brain managed this by creating a semi-hypnotic state where the story's words were treated as reality (more or less) by the brain, instead of first being passed through other functions of the brain (such as critical facilities).

Just ask any high school student about the Titanic; they're much more likely to relate the scenes from the movie than the drab facts they learned in history class. Stories are sticky. Facts are not.

As for why I think story telling is semi-hypnotic: Have you ever watched people watching TV? Most have that zoned-out look to their face that indicates they're engrossed by the story. They are oblivious to the rest of the world until the TV's plug is pulled. (I'd call this hypnotised.) Another interesting point about TV is that people usually watch the flickering story-teller at night, very similar to sitting in front of a flickering fire listening to the tribe's story teller.

This theory also explains why stories usually have morals and knowledge embedded within them.

 


 

The trouble with explorers

(Back to TOC)

4 May 2004

Revised 7-29 May

by Mike Rozak

Recently, Ubisoft announced the cancellation or "Uru Live", an online adventure game. I was saddened to hear the news because I enjoy adventure games, and the online concept intrigued me; particularly what kind of people would be attracted to such a service.

I didn't even get a chance to try Uru Live out since Ubisoft cancelled Uru Live while it was in beta; I was waiting for a released version before trying it. While most of the discussion is about online adventure games in general, any reference to Uru Live details comes from other people's experiences with the beta; Uru Live may have developed into a different beast if it had gotten past beta. Therefore, don't read this essay as post-mortem of Uru Live.

I was interested in Uru Live because I have played a couple MMORPGs, found them to be poorly implemented on-line versions of off-line CRPGs (such as Elder Scrolls: Morrowind or Dungeon Siege), and was underwhelmed. Not only did I get bored of killing orcs after the tenth one (let alone the 10,000th), but MMORPGs tend to attract people whose personalities don't match my own. Off-line CRPGs are only populated by me and a few thousand NPCs; they are devoid of bloodthirsty teenagers.

I have also been considering writing my own online adventure game platform (aka: interactive fiction, or IF) targeted at hobbyist authors. I was hoping to see what problems Uru Live encountered and how they dealt with them. Obviously, they encountered insurmountable problems. From what I've heard, they didn't have enough on-line users to make the venture worthwhile. Since then I've wondered why they didn't get enough players.

Rather than investing a few million dollars to produce my own adventure-oriented online world only to have it fail, I decided to undertake some thought experiments and try to understand the reason for Uru Live's failure. This document is the result. It is not "the definitive work" for online adventure games, but merely intended to propose some ideas and start a discussion.

Multiple models

I have taken too many years of physics. Consequently, I am a strong believer in particle/wave duality. For people that have forgotten their physics, particle/wave duality an odd tendency for light to act as a particle when physicists do experiments that try to prove that light is a particle, and to act like a wave when the experiments try to prove that light is a wave.

I apply this understanding to the modelling the universe in general: I don't believe in a grand-unification theory of anything, since no model can completely describe the universe. I prefer to approach a problem that requires modelling by creating several different models and seeing what each has to say.

For my thought experiments I have used or created several different models. The models are:

1.         Not enough content - The effect of content generation costs on on-line adventure games.

2.         Explorers, achievers, socialisers, and killers - Richard Bartle's model.

3.         Keeping players interested - Since I volunteer at zoos and have dealt with a number of species, I often view humans as intelligent primates. This model tries to gain some insights by mixing primates' interests and virtual worlds.

4.         Achiever vs. explorer content - And how they differ.

5.         The nightclub model - Virtual worlds and nightclubs both involve socialisation.

6.         A God-game made real - Sim City with real life people.

7.         A virtual world is a platform, not a place - A marketing model.

8.         Playgrounds, Disneyland, and the Holodeck - Adding story to virtual worlds.

(Documentation note: From now on I will use the term "virtual world" or "VW" in place of MUD or MMORPG, unless I specifically mean a MUD or a MMORPG.)

Model 1: Not enough content

The original MUD was created by Roy Trubshaw because he "enjoyed single-player adventure games (Crowther and Wood's ADVENT, Aderson, Blank, Daniels, and Lebling's ZORK, and Laird's HAUNT)." (Designing Virtual Worlds, Richard Bartle)

MUDs, as they exist today, are nothing like Adventure or Zork. (I haven't tried Haunt.) While some still retain the text interface, they have almost completely discarded the adventure components in favour of CRPG and socialisation elements.

Why did this happen?

Roy Trubshaw's original MUD was eventually inherited and maintained by Richard Bartle. When asked why adventuring took a sideline to socialisation, Richard Bartle stated that he intended to use MUD more as a social tool than just an adventure game, and suspected that people who wrote MUDs after him just followed his example, perhaps blindly.

Thousands of virtual worlds are now run by hobbyists (as MUDs), and a few dozen by corporations (as MMORPGs). The vast majority of virtual worlds are still more closely related to CRPGs than adventure games.

Could a successful virtual world model exist that emphasises adventure-game aspects but which no-one has yet discovered? The Uru team obviously thought so; but they cancelled Uru Live due to poor attendance.

So why did no adventure virtual worlds exist?

I did a thought experiment... (Just to emphasise, this whole article is one long thought experiment.)

I imagined that I wrote on adventure game (like Zork), and put it online. What would users think of the experience? What features would they request? Here's what happens (in my thought experiment):

1.         The first feature I'd add would be the ability for users to "chat", since it seems silly that people wandering around the adventure game couldn't talk to one another.

2.         Next, I'd add the ability for players to work together on puzzles and hand each other objects.

3.         It takes 20-40 hours to complete Zork (or any adventure game). After that, players run out of stuff to do and stop playing. In an online environment, players would complete the game in half the time (or less) because they'd give each other hints; then they'd run out of stuff to do, but rather than stop playing, they'd start whinging about lack-of-content.

In other words, after working on content for a year, I'd have one week of public release before some players would be asking me for more. So much for my long-awaited holiday...

At this point I have a problem. It takes approximately one man year to produce a text adventure game that keeps someone occupied for 20-40 hours. In a week's time I'd be able to produce 30-45 minutes of content. The average VW player is on 20-40 hours a week. For me to keep them all entertained with adventuring content I'd need to hire a staff of dozens. (By the way, graphical content, such as what Uru Live was producing, is at least 10x more expensive to create; my guesstimate is 1 man year of development for 1 hour of entertainment. Raph Koster presents similar numbers at http://www.legendmud.org/raph/gaming/contentcreation.html.)

So what are the solutions?

1.         Do nothing (but socialise) - If I do nothing (except produce content as fast as I can) users will eventually consume all the content. Most users will leave. Those that stay will sit around and chat. They will request even more chat functionality, so I'll stop working on content and emphasise socialisation features. Eventually, the adventuring component would fall completely by the wayside (except as different scenery for chat rooms.)

I could always keep producing content at full speed, regardless of what features players were using. After a few years I'd eventually get a decent amount of adventure content. The question is: In the meantime many users would be attracted to the virtual world because of the socialisation, not the adventure content. If I suddenly began emphasising the adventure content, would my existing users leave? Would potential adventure-gamers think about visiting when all the virtual world directories indicated my world was targeted at socialisation?

2.         Rely on user created content - Since I can't create all the content myself, I provide tools so the users can create their own content. Some MUD authors took this approach creating MUSH's. The problem with this approach is that 99% of what users create is junk, and if it isn't junk, it will probably clash thematically with the content created by the other users. Chaos ensues and users leave. (Just imagine a Jazz band with 1000 players, only 20 of which are good. For that matter, just imagine a Jazz band with 20 good players, each trying to do their own thing.)

As Andrew Plotkin pointed out, an Uru Live beta-tester, I am overstating the "chaos" aspect. When the author-created content runs out, people will find ways to amuse themselves by creating "content", either through socialisation, creative uses of world physics, or out-of-game venues, such as web-pages. Some of it may even be enough to keep the virtual world alive. Relying solely upon user created content worries me though, because if not managed properly, the user created chaos has the potential to destroy a world.

3.         Automatically generated content (monsters) - Rather than trying to create the content myself, I'd have it randomly generated and spend my time improving the random generators.

The easiest randomly generated content is to create monsters. If I place enough monsters between puzzles in my adventure, then players will spend so much time fighting the randomly-generated content that my 45 minutes of new content every week will take them 20-40 hours to get through.

Monsters introduce some issues of their own; they imply that characters have skills (namely combat skills), and that the more monsters that characters kill, the better they get, and the tougher the monsters must be. I have just (unintentionally) introduced levelling and the levelling treadmill.

Magic spells aren't far behind.

All the loot that players collect, and their need for bigger and better armaments leads to an economy, and maybe even crafting.

Furthermore, if player characters can kill monsters, players will ask for features so player characters can kill other player characters... and in turn, enable griefers. Griefers cause all sorts of other problems that eventually lead to guilds and numerous other constructs that are now commonplace in virtual worlds.

Interestingly, but the time all is said and done, the adventure component of the virtual world disappears and is completely replaced by an online CRPG.

4.         Mix of socialising and monsters - I could also take the direction of supporting socialisation and combat functionality. Or, I could mix socialising and user-created content. (I couldn't, however, mix user-created content with combat because users would exploit the user-created content to make their own characters stronger.) Both of these models exist as virtual worlds.

In "Designing Virtual Worlds," Richard Bartle points out that there are four stable configurations for virtual worlds. Interestingly, these correspond to my four solutions for the "not-enough content" problem:

1.         Type 1: Killers and achievers in equilibrium. (What I have labelled "Automatically generated content.")

2.         Type 2: Socialisers in dominance. ("Do nothing (but socialise)")

3.         Type 3: A balance between all four types, with enough explorers to control the killers. ("Mix of socialising and monsters")

4.         Type 4: An empty virtual world. ("Rely on user created content")

Richard Bartle has some other relevant observations...

Model 2: Explorers, achievers, socialisers, and killers

In 1996, Richard Bartle published a paper, "Hearts, Clubs, Diamonds, Spades: Players Who Suit MUDs", (http://www.mud.co.uk/richard/hcds.htm). If you haven't read it, I suggest you do so before continuing on.

The gist of the paper is that players of virtual worlds (online adventure or CRPG games) fall into four general categories that can be arranged into a 2x2 matrix. These categories are:

One minor point about explorers from Bartle's book, "Designing Virtual Worlds", is particularly important to me: "Explorers are a rare occurrence in virtual worlds."

Why? I'll get to this in a minute.

Note: Throughout the document I use a different definition for "explorers" and "achievers" than Richard Bartle provides. In "HCDS: Players who suit MUDs", explorers are defined as players who try to find out as much as possible about the virtual world, while achievers are people who give themselves game-related goals and set out to achieve them. Instead, I equate explorers to the online equivalent to people that like adventure games, and achievers to the online equivalent to CRPG players. This does twist the model around, in the same way that using a piano to play music written for harpsichord affects the music of Bach. You might even argue that it invalidates the model, although I would disagree. I found such a transposition of the original to be useful to the problem I'm addressing: why online adventure games seem to fail. If my adjustments offend you then pretend there's no connection between what I'm describing and Bartle's model.

My take on player types

Richard Bartle's paper goes on to explain how a virtual world can modify its design to attract different player types, and how each player type affects the other player types. For example: If hordes of killers move into a virtual world, all the achievers and socialisers will leave, shortly followed by the killers (because they have no-one left to kill but themselves.)

Although Bartle lists the reasons why one player type affects another, I thought I'd examine the subject from a different angle and see if the results were the same.

The first question I asked is, "If I'm player type X what do I think of player type Y? Do I want more of them in the game, fewer of them, or don't really care?" This could be tested by identifying players as explorers, achievers, socialisers, and killers, and then asking them if they'd like to see more or fewer (or don't care), explorers, achievers, socialisers, and killers in the world.

Here is my guess at how it would turn out:

Sometimes, what people think they want is not what they really want:

How many explorers are there?

My comparison of "explorers" to "adventure tourists" brings up another interesting issue: How many explorers are there in the real world? How many achievers are there? Socialisers? Killers?

You can answer the explorers vs. achievers ratio by visiting your local game store and seeing how many adventure games are on the shelf compared to the number of CRPGs. In my favourite computer-game store, there are usually one or two adventure game boxes per ten CRPG boxes, implying that there are 5-10x as many CRPG players as adventure game players. Or, in other words, there are 5-10x as many achievers as explorers. (Note: This is only a guesstimate. As a reviewer noted, explorers may take longer to finish a game than achievers, or may not play as many games. Or vice versa. My game store may be atypical. Etc.)

Another way to answer the question is to see where people holiday (assuming they have the money). Do they go to Florida, Spain, or other safe destination, or to search for gorillas in Africa or trek around Nepal?

How many killers are there? This is more difficult because killers only show their true colours when they have power or anonymity. In real life, that means people in management positions and prank phone callers. My guess is that 5%-10% of the real-world population are killers (not literally, but in the player-types sense).

5%-10% of the population are explorers. The rest are evenly divided among achievers and socialisers, 40%-45% each.

Anecdotally, I've observed that some populations are skewed towards explorers, socialisers, achievers, or killers:

Why does the number of explorers in the real world matter? It affects the potential market size for products targeted at explorers. This, in turn, affects what type of companies will target them, and how.

The cost of content

Virtual worlds are in competition with the real world, television, and other virtual worlds. If the content (what draws the player to the virtual world) is not compelling enough, the player will leave. All player types need content. All content costs money.

Targeted virtual worlds

Using the new observations, let me re-explore the fate of virtual worlds targeted at specific player types:

The problem with targeting

Even if targeting a virtual world at a specific category of player works (such as for achievers), targeting a virtual world at a mix of player-types works better for a few reasons:

1.         The explorer, socialiser, achiever, killer matrix is actually a two dimensional continuum. Many people fall in-between explorer and socialiser, or explorer and achiever, etc. Virtual worlds targeted exclusively at one player type will lose those players that fall between.

2.         Players' types (explorer, socialiser, etc.) change depending upon their moods. (For example: While I usually like playing adventure games, I still go for the occasional CRPG.) One reviewer commented that it's worse than this, since player types also change depending upon the virtual world - which is an interesting social engineering subject that influences all virtual worlds, not just those targeted at explorers.

3.         Players have friends who may not all fall into the same player type. If a virtual world is exclusively targeted at one player type (such as explorers) then players will find it harder to get friends to play. If their friends get involved in another (more generalised) virtual world, they are more likely to follow their friends.

This follow-ones-friends issue has other ramifications for virtual worlds, namely that of critical mass. A commonly accepted "rule" of MMORPG marketing is that if it doesn't achieve 100K users in a year its subscription rate will gradually decrease and the MMORPG will fail. Fixed costs are one reason for failure, but people following their real-life friends is a positive feedback loop that causes large MMORPGs to get larger, and small MMORPGs to get smaller. While a virtual world that only targets a sub-set of player types can exist, it's an uphill battle that ultimately reduces the number of players attracted to the virtual world.

Model 3: Keeping players interested

After having a stress dream that the achievers and socialisers of the world got together and kicked out all the explorers because they were useless (similar to how the Golgofrinchians kicked out the telephone sanitisers in Hitchhiker's Guide to the Galaxy), I decided that I wasn't entirely happy with these bleak conclusions.

A few months ago I wrote up a short article about an "Evolutionary explanation for entertainment". See http://www.mxac.com.au/drt/NaturalEntertain.htm. In the article I produced a list of stimuli and drives that hold a primate's (aka: human's) attention.

Below (in the first column) is a list of those stimuli and drives. The other columns show how well these stimuli and drives are satisfied by a number of different entertainments, including TV/movies, adventure games, CRPG, and virtual worlds. The more stars, the better the entertainment fulfils the stimulus/drive. Red stars indicates that the entertainment is the best at this type of stimuli/drive. (You may disagree with some of the stimuli/drives or scores. If so, trying making up your own graph to see if the results change.)

TV / Movies

Adventure games

CRPG

Virtual world

Food

Narcotics

Danger

*

*

**

Socialisation - politics

**

Socialisation - friends

*

**

Socialisation - status

*

*

***

Socialisation - competition

*

*

***

Socialisation - gossip

*

**

Something new

**

**

*

*

Sex

**

*

Ferreting

*

***

***

Hunting and gathering

*

**

**

Migration

*

**

**

**

Money

*

**

Learning

**

*

Creation

*

Exploration

*

**

Problem solving

***

*

*

Escapism

**

***

**

*

Stories

***

*

*

Not surprisingly, CRPGs and virtual worlds are very similar. Virtual worlds equal or outscore CRPGs on every category except two:

Looking at how a CRPG's scores change when on-line functionality is added (turning them into a MMORPG or MUD), one can guess how an adventure game's scores will change. Below is a table listing how on-line functionality can be used to improve an adventure game:

Adventure games

Food

-

Narcotics

-

Danger

Online-content can increase the danger if players are allowed to attack one another, and if there are tangible consequences for losing.

Socialisation - politics

Players will be able to communicate just like in a CRPG virtual world. They may be more limited in their ability to grief one another, diminishing the benefits for status, competition and gossip.

Socialisation - friends

Socialisation - status

Socialisation - competition

Socialisation - gossip

Something new

CCD (See below)

Sex

Virtual sex?

Ferreting

-

Hunting and gathering

-

Migration

CCD (See below)

Money

On-line economics make money more interesting than in off-line play.

Learning

Players can "learn" from one another by having interesting conversations.

CCD (See below)

Creation

Players can share their creations on-line.

Exploration

CCD (See below)

Problem solving

Escapism

If all the players role-play then an on-line system can improve escapism. If player's don't role-play then escapism will be harmed.

Stories

CCD (See below)

For an adventure game, the bulk of the stimuli/drives cannot be improved by going online. Many of the categories are marked as "CCD", which is short for "Cheaper content delivery". It's the only thing that online content has to offer for the given stimulus/drive.

Basically, if putting an adventure game online can make it cheaper (or better for the same cost) then players will go online. If going online does not reduce cost then online adventure games have little to offer, and explorers would prefer to play off-line adventure games.

The perils of online distribution

The Internet promises to be a frictionless (ie: cheap) distribution system. While a game may be sold for $50.00 at a store, the game company only gets (approximately) $15.00 of that due to COGs, distribution, and the supply chain. Theoretically, a game company could just distribute its game on the Internet, bypass all the expenses, and pocket the $50.00.

As seen in the stimuli/drives list, potentially cheaper distribution is the main way an on-line adventure game can improve upon an off-line adventure game.

Distributing on-line comes with a heap of problems though:

What happens when all these issues are added up? An on-line adventure game requires 2x as much development time (because of hint giving and on-line development costs, but lower eye-candy costs), for one quarter the revenue ($7-$10/copy, and no revenue from bad purchases). A subscription service will bring in more revenue, but will also incur on-line service costs and continual need for new content. The number of paying players might increase because of much lower piracy rates and browsers that unexpectedly liked the game.

Model 4: Achiever vs. Explorer content

Throughout this document I've been claiming that achievers are people that like playing CRPGs, and explorers like playing adventure games. Although everyone reading this has played CRPGs and adventure games, I haven't given a detailed description of a CRPG or adventure game. Doing so provides some interesting results...

CRPGs are games involving repetitive tasks with a variations on a theme that result in the character's skills improving, such as killing monsters using various weapons and spells. In other words, "Practice makes perfect." In a CRPG players learn a skill (killing things with a dagger), apply it (kill 100 rats), improve upon the skill (learn how to use a longer dagger), and repeat. The process of applying the skill results in a reward of experience points or money for the player. Both rewards allow the player's character to use new-and-improved weapons and armour, or enter new regions of the world, allowing the character to attack new-and-improved monsters. The reward also acts as a metric to tell the player how well they're doing. (For a detailed description of this process, see "Swords and Circuitry: A designer's Guide to Computer Role-Playing Games", Prima Tech.)

CRPG authors control development costs by having the player kill the same monster 10-1000 times, as many times as possible before the player gets bored with the activity. To prevent boredom, and keep costs low, variations are added to the monsters, world, and player-character's abilities. Instead of attacking rats with a dagger in a house, the PC now attacks "giant rats" with a "short sword" in a "basement". This cycle of "kill monsters", "get bored", and "add new variation" are repeated until the player wins (at which point he/she can start playing over again), or the player gets completely bored and quits the game. Typical CRPGs last 50-200 hours.

The repetition-with-variation model can be applied to more than just CRPGs. MMORPGs use a similar approach for crafting, or in the case of "A Tale in the Desert" for building a virtual Egypt.

Adventure games, on the other hand, involve a world with puzzles that must be solved. Each puzzle is unique; in adventure games, once a puzzle is solved (such as killing Zork's troll with the nasty knife) it is frowned upon to have another puzzle involving a similar solution. (In Zork, the thief can be killed, but the cyclops and bat cannot.) There is no possibility for "practice makes perfect" because every challenge requires a different solution. Adventure-game players are rewarded for their puzzle-solving ability by learning new information or being allowed into new areas of the game, where they encounter bigger and better puzzles.

Adventure games reduce their costs by making their puzzles more difficult, since difficult puzzles require players to (figuratively) bang their head against the wall longer until the solution is discovered. (A typical 40 hour adventure game only takes one hour if the player follows a walkthrough that gives the solutions to every puzzle.) The cycle in adventure games is: "present player with puzzle", "player character wanders around the world looking for clues", "player solves puzzle", and repeat. Adventure games repeat until the player wins or gets bored. (Interestingly, as the player wanders through the same environment time after time, he/she notices new aspects about the world that he/she had missed before and which certainly would be missed in the walkthrough. It's a bit like reading a good book for the second or third time. The only difference is that adventure games force you to re-read.)

Achievers (who play CRPGs) do not like explorer content (adventure games), considering adventure games tedious and unexciting. Explorers do not like achiever content, considering CRPGs to be mindless and boring. This isn't completely true: Some CRPGs include some relatively-easy puzzles (adventure game content), and some adventure games include simplistic combat (CRPG content). The middle-ground, an even mix of CRPG and adventure games, is fairly rare on the PC. (One reviewer, M. D. Dollahite, pointed out that the Final Fantasy series contains a mix of both adventure and RPG, but it's console only.) I'm not sure exactly why the two genres don't mix, but I have a theory:

Induction and deduction

So how are puzzles different than killing monsters?

I was going to write that achievers liked the danger element, but I don't think this is true. A CRPG has just as much danger as an adventure game; namely none. Achievers can pace themselves so they're only every fighting monsters that are clearly weaker, so they have no chance of losing. (The only time they cannot do this is in a PvP game, which is exciting.) Besides, adventure games can kill PCs just as easily as CRPGs, if not easier; Zork killed your PC if his lantern went out.

Some other differences also exist:

I don't think any of these are the defining issue though.

The real difference between a CRPG and an adventure game is the type of reasoning used to solve problems. CRPGs use "inductive" reasoning, while adventure games use "deductive" reasoning. Just in case these terms are a bit fuzzy, or you never took a logic/math course using them, here are some definitions from Dictionary.com:

Basically, induction identifies a pattern, and from the pattern more general conclusions can be drawn. The "mathematics" definition is strikingly similar to my description of a CRPG. "First the theorem is verified for the smallest admissible value of the integer." corresponds to "Start the player character with a dagger and have him kill a rat." "Then it is proven that if the theorem is true for any value of the integer, it is true for the next greater value." corresponds to "Once the PC kills the rat, teach him how to kill a giant rat with a short sword." "The final proof contains the two parts." means "Repeat."

Deduction is what Sherlock Holmes does, aka: an adventure game. "The process of reasoning in which a conclusion follows necessarily from the stated premises" corresponds to "The process of solving a puzzle by combining the hints in the game.". "Inference by reasoning from the general to the specific." means "Using the generalised hints to solve a specific puzzle."

Note: This is a completely different dimension that Richard Bartle uses to differential achievers (CRPG) from explorers (adventure), which is "action vs. interaction". The "world vs. players" axis doesn't even exist in this model, except for the alpha-(fe)male discussion, below.

(Anyone with a mathematics degree is probably cringing at my distortion of the mathematical terms, induction and deduction, into game-play.)

Biologically, induction is often thought of as left-brained while deduction (intuition) is right-brained. Could there be a biological reason explaining the difference between player types? (Women are supposed to be more right-brained than men. I wonder if women are more likely to play adventure games than CRPGs?)

What does induction and deduction have to do with explorers not liking online virtual worlds? I'm not really sure.

Here's a half-baked hypothesis though: Content that's mid-way between pure adventure game and pure CRPG requires both inductive and deductive reasoning. The human brain might find it difficult for both modes of thinking to be active at once. I know that if I'm playing a CRPG and run across adventure game puzzles the "flow" is broken, just as if I'm in an adventure game and run across combat the flow is broken. I couldn't tell you what the "flow" is though, except that maybe it's related to which side of my brain is dominant.

Perhaps a mid-point between a CRPG and adventure game is not advisable. If this is true then content cannot be a mix of CRPG and adventure game, and therefore achiever content must be completely separate from explorer content.

Watering down content

Having explained that CRPGs rely on induction while adventure games use deduction, let me return to my discussion of achiever vs. explorer content.

If an achiever comes across explorer content while playing, they won't find it interesting (or will find it too difficult) and are likely to "cheat" by downloading and following a walkthrough for the game until they get back to the CRPG part. If an explorer encounters achiever content they too may "cheat" and find a way of avoiding the combat, such as getting a gang of friends to safely defeat a monster that an achiever would have taken on by themselves. (This only works in online worlds. In offline worlds an explorer will get bored with the combat and shelve the game.)

If both sides cheat when they get to the content that they don't like then what's the big deal? The problem is that achievers not only like to use induction, they like to "win", which means being the best at the game and beating out all their competitors. Achievers that encounter puzzles and use walkthoughs to save time (and boredom) will level-up faster, obtaining an advantage over other achievers. As a result, all achievers will cheat and use walkthroughs. Explorers that are intent on "winning at all costs" can just as easily use the walkthoughs, but it becomes a very hollow victory. Consequently, there are no explorers that play to "win".

This isn't exactly true, but before explaining why, I must digress.

Let me return to the issue of costs: A CRPG's cost-per-hour-played is controlled by how often a player is forced to fight the same monster. If a player must fight a monster 1000 times instead of 100 times, the CRPG costs 1/10th as much to develop. An adventure game's cost-per-hour-played is controlled by how difficult the puzzles are. The more difficult, the longer it will take players to figure them out, and the cheaper the content. Harkening back to pubs in the wild west, let me call this process "watering down the content".

For both achievers and explorers, the more watered-down the content, the less interesting the game. If an achiever's content is watered down too much they will quit. If an explorer's content is watered down too much they will use a walkthrough to get through the difficult bits, and then quit.

Making the content too easy (killing only 10 rats instead of 100 levels one up, or making puzzles trivial) isn't good either. It makes game development more expensive and reduces the satisfaction players feel upon completion.

Virtual worlds water down content. When I play a CRPG I usually get half way through it before I get bored, playing for about 20 hours. When I played Asheron's Call 2, I played about 40 hours and only got 1/8 way through the content. There was more content (I've heard about 2-3x as much as a typical CRPG), but I also noticed I had to kill more monsters of the same type before moving onto a new class of monster. I'm not the only one; many people complain that virtual worlds require too much work to advance.

Virtual worlds do this on purpose. While a typical CRPG needs to last 50-200 hours, a virtual world must last 400-800 hours (20 months x 20-40 hours/month). After all, in a virtual world, players are paying by the month. Additionally, those virtual worlds that make levelling too easy find that some players quickly max out and then either whinge about the lack of content or leave, neither of which are good.

What happens to players like me when we conclude that the virtual world has watered down its content? We leave the virtual world and go back to playing offline CRPGs. The people that remain in the virtual world are playing for one or more of the following reasons:

What about online adventure games? If they were cheap enough to be feasible, what kind of explorers would stick around for watered down content?

As a result, people that play the online CRPG portion of virtual worlds are mostly achievers (levelling treadmill and power-games), killers (PvP and alpha-(fe)male wannabes), or socialisers.

So what does this say about the feasibility of explorer content?

This model indicates that I can create explorer content as long as I don't expect players to stick around. Watering down the explorer content does retain some player personalities (power gamers), but most will revert to a walkthrough for content that is too difficult.

Can I mix achiever and explorer content? The induction vs. deduction thought-experiment implies that I cannot have explorer and achiever content in the same "quest", but that's a half-baked idea. I could always have two separate quests, one targeted at explorers and the other at achievers. Most players would play only those quests that interested them, However, alpha-(fe)male wannabes would use walkthroughs to cheat on the adventure game content if it provided them an advantage (such as experience points or special items).

Model 5: The nightclub

Online worlds are a lot like nightclubs.

People visit nightclubs to drink, dance, listen to music, partake in velcro suits (or other trendy activity), and to meet other people. People visit virtual worlds to kill things, explore, get a sense of accomplishment, and to meet other people.

Nightclubs are often "themed", being western, pop, punk, under-18, classical, etc. People pick their nightclub based on the theme. Each theme, of course, is associated with a style of music. More importantly, each theme is associated with a style of patron.

One of the reasons I don't like MMORPGs is because they're filled with teenagers (or teenage-minded people). I don't care to talk to most teenagers. I didn't care to talk to teenagers when I was a teenager. The entire MMORPG environment, especially the achiever/killer features, is targeted at teenagers. If I were producing a MMORPG I'd make this decision too, since teenagers are a very large percentage of gamers and their personality works well with MMORPGs.

One of the reasons I was interested in Uru Live was because teenagers would stay away from it, leaving adults (or adult-minded teenagers). It didn't provide the action or reptilian-brained activities that so attract teenagers.

I suspect that if explorer-oriented worlds are ever commercially viable they will be targeted at adults, and achiever-oriented worlds will be targeted at teenagers. Uru Live was clearly targeted at a different demographic than MMORPGs, and many customers liked it specifically because it didn't include teenage-minded MMORPG players. (See Uru Forums) (If this is so, my earlier question about whether it's possible to mix explorer and achiever content is moot; they won't be mixed because each one will be targeted at a different demographic.)

Some nightclubs include another useful "feature" that many virtual worlds already copy; they have a person who stands at the doorway and only lets desirable people in.

Model 6: A God-game made real

In a "God-game", such as Black & White or Sim City, the player pretends to be God. His subjects are thousands of electronic AI's that he must keep happy and healthy. If he doesn't they either leave or die.

Oddly, virtual worlds are God-games for the authors. The virtual world author must act through his virtual world tools to keep his virtual world's inhabitants happy, or they'll leave. Unlike a God-game, the inhabitants of virtual worlds are real people. The God-game nature of running a virtual worlds is a well known to MUD wizards, although not necessarily stated in such a blunt manner.

Of course, real-life players are infinitely more complex than the AIs used for God-games, and it takes more than a few well-placed roads and skyrises to keep real people happy. However, the God-game analogy raises some interesting issues:

First, some players really enjoy playing God, in God-games, and as virtual world authors. A virtual world could include sub-worlds where players act as Gods and invite other players in to enjoy what they have created. This, of course, is player created content. It has a few problems:

I don't have any novel answers to these solutions except for the exploit problem... CRPGs encourage exploits because experience, gold, and items translate from one sub-world to another. (If they don't transfer then many CRPG players won't bother investing time in the other world.) In an adventure game, exploits are much less likely since there is no experience or gold (usually), and items can logically be kept in the world where they were created.

The second observation is this: The real God (or deities of your choosing) uses people to accomplish his (or her) goals, often by "working in mysterious ways" through luck, voices, dreams, and visions. How can a virtual world author (aka: "God") manipulate players to improve the world experience?

When I say paying, I don't mean with real money, but with game money or goods. Here's an example:

Current virtual worlds "pay" players to go on quests, such as killing all the rats in the farmer's basement or rescuing the a lost villager. Payment includes experience, in-game money, and equipment. Many players like quests because they give the players goals, and some additional pocket money.

Virtual world authors should be yelled at by their accountants for such quests. It costs 100 g.p. (of virtual money) to hire a player to kill the rats. This is a waste of money, since the virtual rats don't really need killing. After all, they were generated for the sole purpose of being killed by the questing character in the first place. It's like creating make-work for employees, and doesn't make economic sense.

Why not "pay" players to entertain each other? It's easy; just twist the quests a little. Have one NPC, someone in organized crime, hire players to steal chickens from all the farmers in town, for a cash reward of 100 g.p. The farmers, in turn, hire different players to protect their chickens, for 100 g.p. Only one of the player characters gets the reward. Both get entertained.

The plot can be extended: A third PC, the local mayor, may pay someone 1000 g.p. to knock off the organized-crime PC. When that happens, the market for chicken stealing and protecting dries up until a new NPC fills the old one's place. Maybe a NPC that was previously a pick-pocket is promoted to being the new chicken-stealer. (One reviewer mentioned that http://www.skotos.net/articles contains articles with similar concepts, although I haven't spent the time looking for them.)

Repeat ad infinitum.

Of course, this is very manipulative. Players are being objectified by the NPCs. (Which is probably just, because NPCs are objectified by the players.) Will players like the scheme? Who knows. Some may object, but I suspect many of them will find it more interesting to be part of the larger story that to pout that they're being used by the authors to enhance the virtual world.

The author, playing god, is responsible for programming in the major NPCs' goals. The NPCs, in turn, manipulate the players and drive the world story. When an NPC reaches its goals, or is killed by a PC, the author jumps in and adjusts the story.

What does this second observation have to do with explorers? The web of NPCs hiring PCs to do odd jobs might actually add up to an important story. Maybe a wizard keeps hiring PCs to acquire various magical ingredients. If someone is smart enough to observe, they may realise that the wizard is building a special magical item, or is casting a powerful spell that allows the wizard to take control of the city. Or maybe the wizard is being controlled by the Boy Sprouts, who are in turn controlled by the UFOs. (If you don't get this joke you've never played the "Illuminati" card game.) If the wizard is stopped, do the future events of the world change? This is all explorer material.

The system also requires relationships between individual NPCs and individual players. A NPC will learn how reliable a player is, and give the more difficult tasks to the more reliable players. Achievers will like this, at least until their patron is killed by a griefer or another achiever.

The revised quest system has at least two problems:

1.         It's a bit too much like real life. Most people are already pawns of others in real life, and may not wish to play the same role in a virtual world.

2.         It's open to numerous exploits that may doom it from the start.

Model 7: A virtual world is a platform, not a place

This title isn't exactly correct... To the players a virtual world is a place. To the developer it's a platform. I'll explain why, and how this affects virtual worlds targeted at explorers.

If you think that a virtual world is solely a game, then what I'm about to say won't make much sense. I view a virtual world as a place that allows players to partake in activities, some of which may be games, some socialisation, and other forms of entertainment. The game is just a portion of the virtual world's experience. It's not even one game, but many games, such as CRPG, economics, and flight simulators all rolled into one. If you haven't heard this concept before then do some searching on the web or read "Designing Virtual Worlds." If you don't agree with my assumption that a virtual world is a place, this model won't bear any weight.

Back to talking about "place"... Assume that a virtual world were just a place, such as the Earth stripped of all people, animals, and potentially plants. What could an individual do in it?

This would quickly get boring. (Anyone who doesn't think so has a much longer attention span than I do. For those who don't believe me, try one of the many 3D chat virtual worlds during off-peek hours and see how entertaining vacant worlds are.)

A developer could make the world more interesting by adding sentient creatures, like other players. This would allow for another activity:

When people run out of stuff to talk about, chatting too gets boring. In the real world, bringing people together to chat is called a party. It is accompanied by food, alcohol, and games (such as cards and twister) in order to liven things up. Since food and alcohol aren't viable on-line services, a developer can only use games to make the virtual world more interesting.

Existing virtual worlds have added games that go well beyond card games in scale and complexity. These new games are:

Virtual worlds could also incorporate other computer genres:

Notice the trend: Because a virtual world by itself is fairly dull, developers add various sub-games (and activities) to the world. As players get bored with those sub-games, developers either expand the existing sub-games (such as adding more levels to a CRPG), or incorporate new sub-games (such as Star Wars Galaxies' recent addition of space combat). This becomes an infinite cycle.

Most game genres that have been incorporated into a virtual world originally existed off-line, and are still sold as such: CRPG, adventure, FPS, cards, etc. When an off-line game is incorporated into a virtual world, the user's on-line experience is often inferior to the off-line counterparts:

Virtual worlds offer only a few games that are better on-line than off. Such sub-games rely on large numbers of online users, such as kill-fests, crafting, and the economy.

Despite the inferiority of many virtual world sub-games, people are still willing to pay a lot of money to play. Why?

It's because of synergy. The combination of several off-line games (or free on-line games) into one virtual world produces a better experience. The whole becomes more valuable than the sum of its parts. Here are some examples:

Interestingly, if you apply the synergy concept to an adventure game (such as Myst) you'll see that an adventure game is a virtual world containing sub-games of puzzles. The two elements have a positive impact on each other. The virtual world element gives a purpose to the puzzles. The puzzles make the player spend more time wandering around the virtual world and discovering the scenery. Myst, if broken down into scenery and a set of puzzles, is much less interesting than the whole. (Myst also includes a story component, which I'll discuss later.)

So, let me rephrase the question a bit: How does a virtual world provide value-added to the player?

A virtual world indirectly provides value-added to the player by being a good platform for the developer:

Virtual worlds are "platforms" because the virtual world, which is mostly worthless stand-alone, provides the structure upon which other games are built and marketed.

I can imagine a future where the top three virtual world developers morph into something like cable providers... Consumers pay a monthly fee. For that fee they get to play any of 50-500 games (as opposed to cable providers, which provide 50-500 TV channels). The game company's marketing model would change from a per-unit model to an annuity... If you were a large corporation, would you rather have a risky hit-based model, or a stable and profitable annuity? I know Bill Gates' answer to that question.

In "Playgrounds, Disneyland, and the Holodeck" I discuss a variation on the cable-TV model.

Here is a thought: How many people have more than one cable provider? How many people have both cable and satellite TV? Approximately none. Apply this same thinking to virtual worlds: How many people will pay to be members of two virtual worlds at once?

My Microsoft-trained mind does the math and comes to the following conclusions: In the future there will be three (maybe five) virtual world companies that own 80%-90% of the market. They won't own just the virtual worlds market, either. They will also own most of the retail games market. The Big Three will dominate any customer segment they're interested in; this means mass market. They will dominate any market where throwing money at the problem improves the user experience; such as modelling and animation. (Just think: large virtual world providers = mass market = Hollywood.)

The remaining 10%-20% of the market will eke out an existence by targeting consumers that don't like the mass-market content provided by the Big Three. They won't have enough money to provide the dazzling visuals and animations that the Big Three will. Nor will their worlds be as large. They probably won't even get shelf space on retail stores, so they'll have to provide internet distribution.

Am I right about this? (I hope not.) Would anyone believe me if I were right? (Probably not. People don't like considering dire predictions, especially if they're the ones whose doom is predicted. If they did listen, though, they might be able to protect themselves.)

If you agree with me and follow my line of thought then you'll notice many ramifications for virtual worlds. However, I'm discussing virtual worlds targeted at explorers here. How does all of this affect them?

Model 8: Playgrounds, Disneyland, and the Holodeck

After posting a draft of this document on rec.arts.int-fiction, several reviewers pointed out that I didn't mention author-created stories, and that I didn't discuss some of the future possibilities of virtual worlds. Originally, I hadn't touched on author-created stories because they're not currently possible in virtual worlds.

However, I forgot one very important thing: Stories and adventure games are linked. Except for the first few adventure games, the puzzles have not only been linked into the world, they've been linked with the story surrounding the world and immediate happenings. Some adventure games rely on a story that happens before the game starts (such as Myst or Deadline), while others use the puzzles as a means to advance the story (Syberia).

One reason that off-line adventure game players may not want to go online is that virtual worlds find it much more difficult to include a personalised story; 100K players running around makes plotting a personal storyline very difficult.

Traditionally, virtual worlds are devoid of author-created stories and instead rely on players to create their own "stories" through their activities. While such "stories" are compelling because the player is part of them and influences the stories, they do not compare with a story created by a professional author.

In a large virtual world, personalised (author-created) stories are not possible. Backstories are certainly possible. Stories that affect the world as a whole can also be done. (From what I've heard about the beta of Uru Live, the team was incorporating a running story into the world, much like Asheron's Call does. Neither Uru Live's nor Asheron Call's author-created story can be personalised for every individual though.)

Maybe adventure game players want a more personal story?

If this is so, then online adventure games are still-born. Here's why:

1.         An author writing a linear novel (or movie) can create a very good story.

2.         An author writing a nodal novel (Choose Your Own Adventure), cannot create quite as compelling a story as the linear one because the author can't control all the reader's choices; good stories often hinge on choices. The loss in story quality is (hopefully) made up for the enjoyment of interactivity.

3.         In a face-to-face RPG, the game master can maintain a decent story with six players, even if the players decide to derail the story. The game master, being human, has enough intelligence to either get the story back on track or invent a new one. The game master cannot, however, manage the story if all six players go in different directions.

4.         An offline CRPG or adventure game can maintain a poor story if the player implicitly agrees to stay on track and not try to break the storyline (or if the world prevents the story from being broken). As soon as the player tries to diverge from the current story the whole system collapses.

5.         Many MUDs, such as those from Skotos, provide storytellers (real people) that provide more individualised stories. The storyteller, of course, can only handle so many players, and those players must be amenable to provided the story, just like face-to-face RPGs. (Uru Live had actors that helped develop and personalise the story.)

Analysing the above results produces a few rules:

1.         The more players in a virtual world, the more difficult it is to maintain a story.

2.         The more choices that players have, the more difficult it is to maintain a story.

3.         Players that try to subvert a story make it more difficult to maintain a story.

4.         The only way to mitigate the above difficulties is to have more intelligence behind the story generation, either more storytellers (real people) or a more-intelligent storytelling AI.

A large virtual world is problematical because of the number of players and the number of choices each player has. To provide author created stories at an individual level (as opposed to world-based plotting), the virtual world must either hire an army of storytellers and hope their stories don't collide with one another, or produce a really good AI that can create non-colliding stories for 100K players, many of which will be trying to break the AI for fun and profit.

An army of storytellers is possible, although very expensive; undoubtedly some virtual worlds will cater to this market, but they'll charge more for it. The other approach, AI, requires technology that doesn't exist.

Personalised stories for large virtual worlds seem doomed...

However, approaching the problem from a different direction provides an answer that isn't quite so bleak:

Playgrounds

A playground is a piece of land (place) with numerous mechanical contrivances (world physics) that lets kids (players) interact with one another. A contemporary virtual world is a virtual playground. Instead of merry-go-rounds and see-saws, virtual worlds have combat and trading. Playground PvP involves pushing kids off the merry-go-round, throwing sand, calling kids names, and occasional fights. Virtual world PvP is based on virtual combat, virtual economics, and social abuse.

Adults build playgrounds for kids so that the kids will be entertained and physically worn out after the experience, providing the adults with some rest. Playgrounds also serve a socialisation purpose, teaching children how to interact with one another. Richard Bartle presents socialisation experience as an important aspect of virtual worlds.

A playground has no author-created story, even though stories are created by the kids in the playground. Playgrounds are not usually themed, other than an occasional colour scheme or painted smiley face.

Disneyland

Disneyland is a playground, kind of. Actually, it's a "theme park". However, it has many elements of a playground: lots of mechanical contrivances that are either designed to entertain or make one feel sick. The contrivances, such as Space Mountain, are more complex than a playground, but Space Mountain is essentially a really big and really fast slide.

Disneyland is not designed so that kids interact with kids though. It's designed so parents and kids enjoy quality time together. The quality is so compelling that some people travel half way around the world to enjoy it.

Unlike a playground, Disneyland has an author-created story, or rather, stories. When people wander around Disneyland they are also wandering around many of Disney's movies and television shows, such as the tree-house in the "Swiss Family Robinson" or the castle from "Sword in the Stone". Not only are many of the rides based on Disney stories, but NPCs (such as Mickey Mouse and Goofy) are right out of the stories. Because visitors to Disneyland have been previously exposed to Disney stories, the act of climbing up the "Swiss Family Robinson tree-house" somehow includes the visitors in the story of the "Swiss Family Robinson".

Star Wars Galaxies and Middle-Earth Online are both using the Disneyland model to enhance their virtual playgrounds. They may not do so successfully, but both virtual worlds are only the first generation. Give them time. (Uru Live was also heading in this direction, building upon the stories established in previous Myst adventure games and books. Uru Live also had actors.)

A quick comment about actors: Uru Live hired human actors to wander around their world and give speeches that advanced the world's plot. While this has advantages of realism, it is problematical. Players that were not around when the actor was online felt like they missed out. Such feelings will either cause all players to be online and in the same area where actors will appear (which is a technology issue), or leave players feeling like they're on the outside looking in (for those people living in non-US time-zones). Things can get even worse: As the infamous assassination of Lord British in Ultima Online shows, someone will try to derail the actor's actions just for the fun of it.

In "A virtual world is a platform, not a place" I discussed the possibility of each of the Big Three producing a fantasy, science fiction, etc. virtual world. They could go a step further than this, producing several science-fiction worlds (or one science-fiction virtual world with several sub-worlds), each one based on a different author's works. One science-fiction world could be based on Star Wars, while another on Larry Niven's works, and another from Farscape. The virtual world could either license the IP from the author, or the author could license the space from the virtual world.

Of course, the themed virtual world would be tied into the appropriate books or movies. Players could wander through the themed world, enjoy the themed activities, and even interact with actors playing important characters from the books/movies. The virtual world might even include linear narratives that further the story, presented as cut-scenes or conversations with NPCs. Maybe the Han Solo NPC would relate a short anecdote (a 10 minute cut-scene) to any visiting player about how he acquired the Millennium Falcon.

Is a themed world with cut-scenes enough story to make everyone happy? Probably not, but it's a start.

Holodecks

At first glance, Star Trek's Holodeck is the "holy grail" in virtual worlds, not only because of the stupendous graphics, but because the Holodeck is smart enough to tailor an experience to a user. (It's even smart enough to take over the Enterprise a few times.) Oddly enough, Star Trek's Holodeck doesn't include avatars of users from all over the galaxy, like a MMORPG does; such additional "features" can easily be imagined though.

The Holodeck may seem like the final word in virtual worlds, but such appearances may be deceiving. Even if the Holodeck were possible today, it might not "work". Here's why I have my doubts:

Over the course of the 1980's I wrote a number of amateur games, including text-adventure games, a Wizardry clone, an Ultima I clone, and an adventure BBS. I also did some thinking about where these games were going. What I imagined then was similar to what we have in contemporary MMORPGs. (Actually, current MMORPGs have much better graphics and many more users than I ever dreamed of.)

However, I am dissatisfied with current MMORPGs; they feel soul-less to me. This is not something I anticipated, but is a consequence of having 100K players in a world being run by a corporation bent on maximising sales. While my predictions were right on one level (graphics), they completely missed the mark on another (user-experience).

Twenty years later, I can imagine a Holodeck-generated virtual world, but I don't think a Holodeck will turn out as I expect. Obviously, some technologies won't exist in 20 years: Transporter-fabricated matter is a bit undo-able, so I'll have to settle for a 3D virtual reality headset with a data suit, or a VR room. The AI is also near-impossible, requiring some human intervention in a Holodeck experience. The fundamentals are there though.

I suspect that the Holodeck experience won't be the ultimate virtual world experiene, for the same reason that kids rebel against their parent's dreams of having them become a lawyer or doctor. Simply put, people don't like being told what to do. They like being manipulated even less. Any attempt by an AI or real person to impose a story on a virtual world is an act of manipulation, even though players may wish for some form of story.

Game masters in face-to-face RPGs know they walk a fine line between creating a good story and forcing players' hands. Players that feel like they've been wronged will make a fuss and/or leave the game. Part of the reason that face-to-face RPGs work, and holodecks may not, is that the player and GM know and trust one another; If the player feels they're being forced to do something they don't want to, they will be more forgiving because they know the GM has their best interest at heart, and making a stink could hurt a friendship. Similarly, the GM knows enough about the player to know what buttons not to push. Getting an AI or paid professional to fill the role of friendly GM is very difficult, although not necessarily impossible.

I can imagine myself going along with the Holodeck story some days, while spending other days seeing how far I can go before I break the AI or make it go crazy. What happens if I replay the murder mystery and hide the murderer's weapon? If I jump off a cliff, what does the AI do to realign the story, have Superman catch me? Basically, I'll turn the original virtual reality game into a new game, push-the-AI.

Does this mean that holodecks won't "work"? Maybe they will and maybe they won't. Unexpected effects are bound to appear though. The potential problem of players resisting story-manipulation is only one example. More issues are undoubtedly lurking.

Mix and match

A virtual world doesn't need to pigeonhole itself into just being a playground, or just a Disneyland, or just a Holodeck. It can be a combination of all three, using elements where appropriate.

The "playground" aspect of a virtual world is the world's physics and mechanics, determining how players (and NPCs) can interact with one another. Playground "stories" are all player generated using the world's physics. Contemporary MMORPGs are virtually all playground. Uru Live had relatively little playground, other than chat and cone soccer.

The "Disneyland" dimension is the amount of author created stories. Being created by a person, the stories are expensive, so must be reused by players. As a result, either the story must be outside the player's control (such as story that happened before the game began, as in Myst), or the player must be willing to follow an essentially linear story path (Syberia).

Unlike a traditional adventure game, the "story" doesn't have to limit itself to one subject. Hundreds of stories can populate a world, creating a world of stories, much like Tolkien's Silmarillion or Australian Aboriginal Dreamtime stories. (Most Dreamtime stories are associated with places, so as Aborigines move through their countryside they are also moving from one Dreamtime story to another.)

Finally, the "Holodeck" dimension of storytelling is the amount of intelligence (human or AI) used to provide a more personalised story. For a long time to come, this will be a person, not AI. Although, a simple AI or well-built tools could aid the human storyteller. Traditional pen-and-paper RPGs use this form of storytelling.

Each form of story has its own advantages and disadvantages: Playground stories are compelling because the player is part of them and creates them, although the stories are not refined. Disneyland stories are refined and polished, but immutable. Holodeck stories are a compromise, allowing users to affect the story, but at the expense of story quality and human intervention, which introduces an element of conflict between the player and the storyteller.

Different players prefer different story types. Providing only one form in a virtual world will alienate some players.

For example: Many off-line CRPG players accept a world with only playground stories (Diablo or Dungeon Siege - both of which are very weak in the author-created story department). Such players have no problem adapting to a MMORPG (which lacks Disneyland and Holodeck style stories). Since most adventure games include a strong author-created story component, I suspect adventure game players prefer author-created stories (Disneyland or Holodeck-style). Consequently, they're alienated by the playground-style stories in MMORPGs.

Not modelled: Poor launch for Uru Live?

Uru Live was officially cancelled because not enough users signed up. However, part of the reason they may not have had enough users is because of a poor launch strategy: Uru, the retail package, was available on store shelves in November 2003, just in time for Christmas. Uru Live, the online portion, wasn't scheduled to go public until February 2004.

Uru Live (online) shipped after Uru (offline). In the user's mind, Uru was synonymous with Uru Live. Users probably expected that when they installed Uru, they'd have access to Uru Live; I know I did. This wasn't the case, since the team (or at least marketing) considered them two separate beasts. Therefore, Uru had shipped, but Uru Live was in beta until February, and perhaps later. This is a bit confusing for users, even me, and I work in the computer industry.

I suspect that management decided to ship Uru "separately" from Uru Live so that Uru would be out in time for the Christmas market, even though Uru Live wouldn't be ready in time.

This decision had several ramifications:

1.         Loss of marketing momentum, since much of the hype over Uru would have dissipated by the time Uru Live was ready.

2.         People that purchased Uru expecting to play Uru Live right away would be upset, producing expensive support calls and user dissatisfaction.

I resemble this item. In Australia, the Uru package didn't include the registration code I needed to sign up for Uru Live. I called the computer store where I purchased the game, only to learn I had to call a 1-900 Ubisoft number, which would cost me money, four or five dollars I think. After two calls I eventually got my registration number, only to learn that Uru Live wouldn't be available until February.

3.         By the time Uru Live would ship, many Uru players would have finished Uru and moved onto other games. Getting them to come back and sign up for Uru Live would be difficult. If beta took longer than expected, which seemed to be the case from Uru Forums posts, then getting users back would be even more difficult.

4.         Some users may have known about Uru Live's delays and not bothered to sign up for their free month until Uru Live was officially released. After all, why waste a free month? The Uru team would have no idea how many users were waiting in the wings. (This is exactly what I did.)

5.         Because the game contained both an online and offline component, some people may have left the online portion until they were finished with the offline game. Again, why waste a free month? And again, the Uru Team would have no idea how many users applied this strategy.

Combined wisdom/folly of the models

Now that I have examined explorers from every possible angle, does this thought experiment give me any useful information?

Here are some possible ways to get explorers using on-line virtual worlds:

1.         Make content creation as cheap as possible (easier said than done), and/or allow blessed users to create their own content (fraught with problems).

2.         Produce puzzles that still require work to complete even if the solution is spelled out to a player in a walkthrough. This could be accomplished using automatic content creation or dynamically changing puzzles.

3.         Reduce the player's cost by eliminating the retail package. (This then requires a small download and/or broadband.) Price the game so that the player sees the monthly fee as good value for money.

4.         Produce a world with private regions, or a mechanism so explorers will know how crowded a region is. If a region is too crowded they can spend their time exploring an emptier one.

5.         Emphasise exploration and socialisation. Limited achiever and killer functionality could be provided, but it may not be worth the development, content, and headache costs.

Some less-obvious solutions might also work:

1.         Find a business model that allows players to stay for only a couple of months, or only play for a short while (5 - 10 hours) each month. (So much for a monthly fee. TV-style ads won't work because the online service will only be paid for click-throughs, not eyeballs. People are not likely to click on an ad and interrupt their immersion. In-game advertising could work for modern or futuristic worlds.)

2.         Since players will only log in once in awhile, provide automatic E-mail alerts to players when new content is added. (Think weekly/monthly episodes, just like TV and E-mail being the TV-Guide.)

3.         If players only log in a few hours each month, they won't build any social contacts, and the socialisation features wouldn't be used (except for people that know each other outside the virtual world). This could be solved by co-marketing with several other virtual world providers and encouraging players to distribute their play time amongst them, perhaps one night a week in each. Encourage players to play on the same night each week so the same faces will be around. (Sounds even more like TV.)

4.         Expect and encourage explorers to make on-line friends and meet up with them in the different worlds. (Do not expect explorers to bring their real-world friends into the game.) Provide "dating" features that let explorers group up based upon which content they haven't yet explored.

5.         Include "games of skill", like card-games and chess. The allow for limited PvP and encourage socialisation.

6.         Find a way to include more "story". Adventure games, CRPGs, and virtual worlds are all beaten by TV's story-telling ability. Adding stories may draw more users into the game. (The stories do not necessarily need to involve the players' characters, but can be about the world's famous NPCs.)

7.         Use the adventure-game nature of entertainment to weed out undesirable player personalities. (Or just have someone that only lets desirable people into the game.)

8.         Either encourage or enforce role playing (to benefit immersion and escapism), or admit that users will socialise out-of-context and add more NPCs (with in-context conversations) to repair some of the lost immersion.

9.         Have the NPCs manipulate the players into role playing.

Would enough explorers be attracted to a virtual world even with the above changes? I'm not sure.

From my perspective there isn't much of an alternative. Achievers, killers, and socialisers are having their needs catered to by 100+ MMORPGs and 1500+ MUDs. The explorers are left out in the cold.

Interesting links...

Thanks to...

Thanks to the following people for reviewing and commenting on this awfully long document. (Said following people do not necessarily agree with all or any of my conclusions. You can read their responses in rec.arts.int-fiction, under the "Thought-experiments about the failure of Uru Live" thread begun 5-May-2004, and "Design: Online adventure games" in https://www.kanga.nu/lists/listinfo/mud-dev.)

 


 

Story and plot vs. freedom in virtual reality

(Back to TOC)

1 June 2004

by Mike Rozak

Computer generated virtual realities seem to have a dilemma which has been bothering me for some time: If you allow players to do anything they want, the world will be devoid of story and plot, feeling intellectually empty. If you enforce a story and plot, players will feel trapped.

I have a potential solution to this problem, which I'll get to later. First, let me build a foundation on which to base my solution.

Space-time - It's not just a tree

As everyone who has watched Star Trek (or any science fiction) knows, contemporary space-time theories propose that time is not linear, but a series of branches. A branch occurs whenever a decision is made, such as flipping a coin. If the coin comes up heads, one reality comes into existence. If tails, another. Every time a new decision is made a new branch appears, ad infinitum. (And as anyone who watches Star Trek knows, one of the branches includes an evil Captain Picard.)

I subscribe to this somewhat... The problem with the theory is that there are an infinite number of decisions happening all of the time (not just when coins are flipped), and most of the decisions result in more-or-less the same reality.

For example, if I were to flip a coin and then put it in my pocket without looking at it the result of the flip wouldn't change the future detectably either way. There might be some subtle changes, but they would be very minor. Under such circumstances I wouldn't view this as a branch, more as a splitting of the timeline and then rejoining. Although, the two timelines (heads or tails) might not strictly rejoin, but they'd be so close together they could be thought of as the same. To use another Star Trek example, if a mad scientist were to knock me from the heads reality to the tails reality, I'd never notice the realities change. (Maybe this is why pens and keys always go missing.)

So, instead of the timeline being a pure tree, it allows realities to reconnect. Technically this is called a "rhizome".

My other contention is that decisions are made all the time. In some realities I may have flipped the coin 1 microsecond earlier. Furthermore, the probability of some events is more likely than others. The infinite possibilities change the nature of the rhizome. The branches of rhizome become fuzzy from subtle variations of the same reality, and vary in brightness due to the probabilities of a path being taken.

In fact, the tree isn't really a rhizome, it's an infinitely dimensional space (which I'll discuss shortly) filled with varying densities. Most of the space is empty, or nearly empty. Thinking in 3 dimensions, it looks mostly like a rhizome (or cob-webs), except that a real rhizome (or cob-web) can't get thin enough to represent the really unlikely events.

Space-time - Infinitely dimensional

Contemporary thought assumes that space is three dimensional and time is one dimensional (except for the alternate realities part, which is someplace between one and two dimensions - called a fractal.)

In simplified terms, the three dimensions of space define a relation between objects (molecules, atoms, etc.). This is called a "spatial" relationship, since it says how far apart two objects are, along with their directions.

Other relationships exist between objects, such as whether object A has ever been "near" object B. Or, to make a more extreme example, whether object A (let's call it Frank) is in love with object B (Jenny).

Interestingly, while physics are interested in special relationships between objects, stories are interested in less quantifiable relationships (like love). I'll discuss this in a bit.

There are practically an infinite number of ways that any objects can be related since a new type of relationship (distance, love, colour, speed, etc.) can be invented at any moment. To compound this, in a universe there are practically infinite number of objects

You can use this (practically) infinite number of relationships to create an infinitely dimensional space, using one dimension per relationship. A snapshot of the universe can be boiled down to one point in this infinitely dimensional description of the universe.

In the likely event that you can't imagine infinitely dimensional space, just pretend it's a 3d-dimensional volume in the shape of a cube. One axis is "How much Frank loves Jenny", the other is "How far away Frank is from Jenny", and the third is "Time." You can use a point to represent the current state of the universe at any time.

Now, imagine that Frank is sitting in his room at 1 PM, at which point he doesn't love Jenny at all. The state of the universe is (0 love, 1 km, 1 PM). He spends half an hour walking to Jenny so he can talk to her. If you trace the point it will create a line from (0 love, 1 km, 1 PM) to (0 love, 0 km, 1:30 PM). Frank then spends half an hour talking to Jenny, and falling in love. The line now traces to (1 love, 0 km, 2:00 PM). Frank walks back to his room over the next half hour, tracing the line to (1 love, 1 km, 2:30 PM).

What I have just described to you is a "narrative", a linear description of what happened (or what will happen).

If Frank (and Jenny) have free will then in comes coin flipping (and probability). Maybe Frank decides to stay in his room between 1:00 PM and 2:30 PM, never talking to Jenny. You could add another line to the universe, running from (0 love, 1 km, 1:00 PM) to (0 love, 1 km, 2:30PM). Or, Frank may spontaneously fall in love with Jenny even though he never meets here, creating a line from (0 love, 1 km, 1:00 PM) to (1 love, 1 km, 2:30 PM). This scenario is extremely unlikely, so create only a very dim line.

Note that Frank cannot suddenly teleport to Jenny, so there is no line from (0 love, 1 km, 1:00 PM) to (0 love, 0 km, 1:01 PM). This is called "physics"; there are just certain things that can't be done in a universe, or at least which are extremely unlikely. (The evil Captain Picard could suddenly get the urge to teleport Frank to Jenny though.)

Repeating the process produces another rhizome, with paths (in infinitely dimensional space) connecting possible realities. (While this description of the Frank's universe is uninteresting, it simplifies the infinitely dimensional universe down enough that the Human mind can imagine it.)

So what does all this mean?

1.         A narrative (linear description of what happened or will happen) is produced by picking a starting point on the rhizome and following it, usually through positive time.

2.         A virtual reality (freedom of choice) exists when the user can choose which paths of the rhizome to follow. Usually, the user will be forced to follow positive time, but in virtual reality, going back in time to alternate realities is also possible.

3.         The holes in the rhizome are known as the "physics" (or logic, self-consistency, etc.) of the world. Some states cannot exist.

Stories and space time

Infinitely dimensional universes are all fun and games (until someone loses their mind), but what does this have to do with story and plot vs. freedom in virtual worlds?

As a stated above, a narrative is what happens when you follow a path in the rhizome.

Of course, given a universe, there are an infinite number of narratives because you can traverse the rhizome in an infinite number of ways. Most of these are not well suited for a story. Either they are boring (see "An evolutionary explanation for entertainment" for my definition of boring) or they lack plot (discussion follows).

What is a narrative's plot? If you ask 100 people you'll get about 200 different answers.

Here are my two answers:

1.         A narrative with a plot is one in which all of the elements of the narrative lead in the same direction. Conversely, a narrative without a plot has elements that wander around aimlessly.

2.         Or, for a stricter definition: A plot is the shortest path between the starting state of the narrative (in the infinitely dimensional rhizome space) and the ending state.

Since definition 1 is completely vague and fuzzy, I'll expand on definition 2 a bit.

For some reason, people like to feel that there's a purpose to life, and hence, everything that happens (or at least most things) somehow leads towards that purpose. When we reach the purpose, the story should end. If elements of narrative do not serve to reach the purpose, they're seen as distracting and uninteresting.

The most purposeful movement is a straight line. In this case, from the point representing the start of the story to the point representing the end of the story. This has two problems:

1.         It would probably make for a short and very boring narrative. Not only is a straight line is the shortest (and quickest) distance between two points, but travelling in a straight line is pretty boring. (How many people take scenic drives on long straight roads? They usually take the windy ones.)

2.         The physics of the universe won't allow it. For example: Perhaps the "plot" of the story is for Jerry to fall in love with Susan. The straightest line is from (0 love, 1 km, 1:00 PM) to (1 love, 1 km, 2:30 PM). While this is possible, it is highly improbable.

Since the narrative's path cannot go in a straight line, it must wander a bit in order to make for a longer and more interesting narrative, and to take the most probabilistic route that gets the the end. (If an author takes an improbable rout, such as teleporting Jerry directly to Susan, the readers will be very angry because the author has just broken the laws of physics. Or at least the physics that the readers think the story is using.)

Conversely, if the path wanders to far away from the straight line, this also bothers readers.

For example: If Jerry, for no particular reason, first took a short walk away from Jenny, this would disrupt the plot because it lengthens the path though the rhizome even though the physics of the universe would allow for a shorter path.

The reader's need for entertainment (interest) is at odds with the plot. The plot wants to get from point A to B as quickly as possible (assuming it doesn't break the physics of the universe). The reader's desire for entertainment wants a car chase and a fight scene thrown in.

A traditional author's job is to find the best narrative, the one which is most interesting and with the best plot. (They need to do lots of other stuff too, such as inventing the universe and actually writing the words.) Obviously, this is a difficult job because there are so many possible narratives, 99.999% of which are either boring or have a lousy plot.

Virtual reality freedoms

A virtual reality uses the same universe as the author, but instead of the author choosing the narrative, the player does.

The good thing about letting a player choose his/her path along the rhizome is that the choice is itself interesting, improving the player's experience.

The bad thing is that the player will inevitably choose the paths that lead to uninteresting narrative or narrative that veers away from the plot. They choose the wrong paths, not because they want to be bored, but because they don't have a complete vision of the world and its physics. Since they can't accurately predict what will happen if they take an action, they can't predict if it will result in an good narrative. And if they could predict which path was best, it wouldn't make for a very interesting experience since they would already know what will happen.

This poses a bit of a problem. Free choice makes the experience more interesting, but it is more likely to destroy the experience because the player will make all of the wrong choices.

Let me abandon Frank and Jenny, and go to a rich world that everyone knows about, Tolkien's Middle Earth. What would happen if the player were to take on Frodo's role? What would the player do?

Here's an extreme example: The scene is just after Gandalf tells Frodo that his ring is the one ring. Frodo offers the ring to Gandalf, but then Gandalf refuses. So what does the player, as Frodo, do? Muster up his bravery and offer to take the ring to Mordor? Maybe, but...

At least one player will flush the ring down the toilet. After all, if the wring wraiths can't find the ring then Sauron won't do as well in his battles. Unfortunately, this makes for a rather poor story, both uninteresting and one that veers away from the plot. (Which is the destruction of Sauron and the writing of another tail for Middle Earth's history.)

I think most people would agree that flushing the ring is a bad idea, but what are the solutions to this problem:

Of all these solutions, taking a detour in the plot and redirecting the plot are most satisfying for the player because they don't break the physics of the universe, still result in the narrative having a plot, and don't constrain the player's reactions.

Both solutions are both very difficult, time-consuming, and basically impossible. Kind of... they're impossible for a pre-programmed virtual world. They're entirely possible if a human is monitoring the player's actions and tweaking the world in response.

The easiest programming/authoring solutions to the freedom problem either remove too many freedoms from the player, or result in an unsatisfying story.

A virtual world without interest or plot

Since maintaining plot and user's interest in a virtual world is extremely difficult, some categories of virtual worlds have found a way to be plot-less and uninteresting. These include virtual chats, computer RPGs, and MMORPGs.

Well, perhaps I'm being a bit harsh. They may have some plot and some areas of interest.

Virtual chats let are basically virtual worlds in which a player can wander around, build scenery, and talk to other players. What makes them interesting is the socialisation and the creativity of building. There is no overall plot, although individuals may have their own self-appointed sub-plots, such as trying to gain social dominance.

Computer RPGs are virtual worlds with a bit of plot (the player must kill the bad guy) and a very complex physics engine. A traditional story makes itself interesting by providing intricate webs of interactions (and relationships) between the characters and the world. The complex relationships allow for a complex rhizome, maximising the chance of creating an interesting and plot-filled narrative. This is far too difficult to program. Therefore, a RPG only allows for limited types of interactions between the player and computer-controlled characters, namely killing them. RPGs do provide thousands of different ways to kill though, which is part of what makes them interesting.

MMORPGs are a combination of a virtual chat and a computer RPG. MMORPGs find a plot virtually impossible to implement because not only is the player making choices that work against the plot, but there are thousands of them. After all, only one player can posses Sauron's ring.

These virtual worlds do exist without a plot and very little of interest. They survive because:

1.         They provide a challenge (killing monsters, find treasure, and going up levels).

2.         They include socialisation.

3.         The act of exploring the word, and the rules the govern the world, is in itself interesting. (At least to me.)

But, they are time wasters. Whenever I finish playing one I don't feel as though I've experienced anything profound or learned anything. (Which is partly because they don't include a plot.)

In the mid 1980's I ran an "Adventure BBS", which is basically an early version of a MMORPG. My experience with it went as follows:

1.         I first implemented a traditional BBS, which just allows people to chat. (Aka: Virtual chat).

2.         I decided to add the adventure part (RPG) because the programming interested me, and I thought people would like more types of interaction than just chatting. (Aka: Virtual chat + RPG = MMORPG)

3.         I created some content in the form of monsters and dungeons for players to explore.

4.         Players quickly worked their way through my content.

5.         The players then used the chat functionality to whinge about the lack of content.

Overall, it was not a successful endeavour. (I supposed that the RPG content did provide more reasons to use the BBS though.) A good part of it was my fault, but some of the fault is inherent in the system.

RPGs (and MMORPG) rely heavily on automatic content generation. This is often why they become uninteresting.

Human-generated vs. automatic content

One aspect of narrative that I haven't explicitly talked about is "content". In general terms, it's what makes a book or virtual reality interesting, since it's the human contribution to the complexity of the world.

The content includes all the artwork, characters, objects, history, etc. It also includes the software, since software is human generated and can apply to the story, although people do not usually think of the software as content. Basically, it includes everything, except the mechanism used to express the world to the reader.

As a general rule, the more content the more an interactive fiction will hold the reader's interest. When the content runs out (has all been viewed or understood) by the reader the interest in the interactive fiction generally ends. Of course, not all content is created the same. Some people are very skilled in content creation and can do much with a paragraph, image, 3d model, or line of code than others.

In terms of the rhizome, the content is the collection of relationships (dimensions) and how the rhizome is arranged to maximise reader interest and sense of plot. How the world is expressed to the reader (though text or graphics) is not part of the content, although without it the content would not be accessible. (A book, by itself, is not interesting. The meaning of the words contained within the book is the interesting part.)

Why one rhizome is interesting and the other not is largely a human issue. That's why humans design the rhizome, and why it cannot be automatically generated.

My last statement is somewhat erroneous. Parts of the rhizome can and are automatically generated. For example, the physics of a virtual world is usually software, although the rules for the physics are human generated. Likewise, elements of the universe that are not important individually, but which may be important as a whole, do include some automatic generation. This includes details of landscaping and foliage placement. Many systems automatically generate characters. RPGs and MMORPG do this to create an unending supply of monsters to kill.

Automatically generated content has a problem though:

Automated content is only as interesting as the work and thought involved in producing the code that automates it. Why? Because once a player has intuitively determined the underlying automation algorithm the system becomes predictable (even though it may be random) and is no longer interesting.

This statement is not true if the player becomes obsessed with the challenge of beating the system. For example: In RPGs/MMORPG the generation of monsters is obviously automated, yet some people will go on being entertained by them not because the automatically generated monsters are entertaining, but because the goal (high level/status) is so desirable.

Theoretically, an automated system could be build that's complicated enough to generate interesting content. This hasn't yet been done.

If it could be done, an automated system could solve some of the problems resulting from freedom in interactive fiction. One reason why freedom-of-choice is very difficult for an IF author is that the player can and will do anything, or go anywhere. When the player comes to the end of the content, the author is forced to create some sort of barrier. Ultimately, this breaks the physics of the universe and makes the player feel walled in.

One obvious use for automatic content creation is the edge of the world. Many authors simply put an impenetrable barrier (mountains) at the edge of the world, informing the player that they can't go any further. If the world is deemed large enough (by the player) then this isn't much of a problem. Unfortunately, creating a sufficiently large world requires a lot of content, even if much of the content doesn't pertain to the plot.

Automatic landscape generation can be used to easily and quickly create a sufficiently large world, or even to extend a world during game play. Because it's automated, the content isn't very interesting, but it's either uninteresting content or wall in the player.

The concept of automatic landscape generation could also be extended to include automatic city generation, along with the city's inhabitants.

Going even further, the universe could all be automatically generated and then peppered with human-generated content that then becomes the thrust of the narrative. If the player choses to avoid the human-generated content (and hence human-generated plot) then that's up to them.

The automatic content could be guided by some broad human strokes. Parts of the world could be marked as "swamp" and others as "mountains", letting the automated systems fill in the details.

Much of automation is a holy grail, and although desirable, largely unattainable.

"ThePlot" object... or a virtual God

Automated content can theoretically form an infinitely large universe that is at least somewhat interesting. If the algorithms are complex enough it will take a player a long time to intuitively figure out how they work. However, automated content generation does not lend itself well to a plot. The automated worlds that I have experience have been largely plot free.

A human mind is the best way to achieve a plot. Unfortunately, players have a way of messing up the plot (such as flushing the one ring down the toilet).

Can plot be automatically generated?

Some MMORPG have sub-plots called "quests". These are fairly simple goals for the player to achieve, such as recurring someone's cat, killing a troll that's charging people to cross a bridge, etc. A few even include automatically generated plots. I haven't been too impressed with the automatically generated plots, but the idea has potential.

Basically, an automatic plot generated modifies the content to create a plot. The system finds a helpless character that's standing next to a tree. It then places a cat in the tree and implants and idea into the character's mind that the cat is the character's own and needs to be rescued. The same concept can be used for the troll in the bridge.

Note that while the specifics of the plot are automatic, the code that generates the plots is written by a human. The plots are archetypal - with maybe only 20-50 types of plots available, but because bits and pieces are varied all the time they come in infinite varieties.

As described, it's easy to imagine the software that could create quests. These are only simple plots though. How could a much larger plot be created automatically?

I'm not really sure, but here's an idea.

When an author creates IF he/she creates code for all the objects (people, places, things) in the world. Each object has code associated with its own behaviour.

An author could also create a "ThePlot" object, or to be blasphemous, "God". The purpose of "ThePlot" object is to monitor what's going on in the world and make it as interesting and purposeful as possible for the player.

Using the Tolkien example, "ThePlot" would notice that the player had flushed the one ring down the toilet. Realising this, it would somehow modify the content (usually in subtle ways) so that either the player will eventually recover the ring, or play can continue without the ring.

Likewise, "ThePlot" could identify that the player has become bored (not playing the game for awhile) or stuck, and subtly modify the content to make things more interesting.

Sounds good? I haven't the faintest clue how to do it.

Reality?

Here's a deeply philosophical thought for you: A virtual world is, in part, a simulation of the real world. If automatic content generation and "ThePlot" need to exist for a virtual world to be successful, do they also exist for the real world?

 

 


 

Why text adventures aren't commercially viable

(Back to TOC)

1 June 2004

by Mike Rozak

Recently I've been pondering the following question: Why aren't text adventures commercially viable? The obvious answer is that they use outdated technology, have no graphics, and hence are like watching a black-and-white 50's TV show on a high-definition TV with surround sound. This is true to an extent, but it isn't the whole answer... Many people still watch 50's TV shows despite their colourlessness. Not nearly as many people play old text adventure games.

This paper discusses some of the reasons why text adventures are no longer commercially viable, and what lessons can be learned.

History of adventure games

First, let me provide a very brief history of adventure games:

Late 1970's

The first adventure game, Adventure, was created by Will Crowther in 1975. It was fairly similar to what is now referred to as a "text adventure" except that it had a simpler parser, no plot and only a few puzzles, and it required a very expensive computer to run.

Early 1980's

The early 1980's were the golden age of text adventure. During this time the classic Infocom games such as Zork, Deadline, Trinity, and The Hitchhiker's Guide to the Galaxy were produced.

A second form of text-adventure began to appear around this time, the multi-user dungeon (MUD). Roy Trubshaw and Richard Bartle wrote the first on-line text-adventure. Interestingly, the on-line adventure transformed into a completely different beast, but that is another tale. See http://www.mxac.com.au/drt/TroubleWithExplorers.htm for more details.

Late 1980's

By the mid-80's every PC manufacturer provided graphics, and adventure games took advantage of them. The later part of the 1980's saw the rise of the graphical adventure and fall of the text adventure.

One form of graphical adventure was pioneered by "Mystery House", which was basically a text adventure with static 1st-person graphics (mostly stick figures) bolted on. Because the graphics occupied so much of the screen the text elements were cut back to only a few lines. The parser was inferior to Infocom's.

The second form was popularised by the King's Quest series. The screen was filled with a static background-image of the room, and the player was allowed to move a sprite-based character around the room. Text was still shown on the screen, but it was secondary to the image.

Early 1990's

Commercial text adventures completely disappeared. First person graphical adventure games faded, while third person games (King's Quest) dominated the market. Graphics improved, but little else.

Late 1990's

1995 was an important year for adventure games with the introduction of Myst. Myst returned to the 1st person view with stunning pre-rendered 3d-graphics and sounds. It excluded all text except for diary entries. Commands were no longer typed, but instead a single click of the mouse appropriately manipulated the object it was clicked on. Parsers no longer existed.

The late 1990's also saw a short-lived trend in adventure games where a game was constructed from a series of short video clips. As in Myst, user input consisted of mouse clicks. The most famous video adventure of the time was Phantasmagoria.

Third-person graphical adventure games seemed to fade away.

Early 2000's

2000-2004 has provided a few styles of adventure games:

First-person games like Myst are still around, although not doing terribly well.

Third-person games derived from King's Quest are once again a successful category. The backgrounds are pre-rendered 3D images, while the characters are rendered real-time with 3d accelerators. The most popular such game is Syberia.

First-person 3D accelerated adventure games, such as Uru, are just beginning to appear. All graphics are rendered real-time using the 3D graphics accelerator. Users move around with keystrokes and can manipulate the world by clicking on objects.

So why do text adventures matter at all? Aren't they the deceased ancestors of modern graphical adventure games.

Yes.

And, no.

While graphics have greatly improved over the last two decades, adventure games have lost some important properties as a result of losing language (text being the means to convey the language):

This loss of capabilities bothers me. Stunning graphics are certainly a good thing, but I get the uneasy feeling that the genre has gone backwards. (It's not just adventure games. Other computer-game genres have followed a similar path. The fundamentals of CRPGs haven't changed much since Wizardry, also from the early 1980's.) I wonder if this is how the Romans felt as their empire crumbled and civilisation decayed into the dark ages.

A fresh look at text adventures

While I was pondering these thoughts, I decided to take a look at old (1980's) text adventures and new text IF to see if my memory of them wasn't overly rosy. I hadn't played a text adventure game for 15 years.

I downloaded and installed a few of these games only a few minutes. (I remember when 150 KB took hours to download at 300 baud.) I excitedly ran the game and was...

Well, I was bored.

My memory of text adventure games was a bit distorted, and I had gotten used to all the snazzy graphics and sounds of modern adventures. The sophistication of the parser, and the "depth" of the text adventures did impress me though.

Here's a summary of my thoughts about text adventures as seen through my 2004 eyes:

1.         I have become used to graphics and sound, just as I am used to colour on my television. Going back to mere text and utter silence felt like a step backwards; maybe my attention span has shortened and I'm addicted to the stimulation.

2.         Reading text off a computer monitor is difficult. As I recall, it was easier in the early 1980's because I used a green-screen monitor (as did most people). Green (or orange) screen monitors use a slower phosphor than televisions, which means that there's no flicker. (They sucked for fast-moving games though because the image would stay ghosted for half a second after it had moved on.) Modern monitors flicker at 60-80 Hz, which still makes text difficult to read. Even with a slower flat-panel display some flicker is noticeable.

The other (minor) issue is that the text adventures I tried defaulted to small type and only occupied a small portion of my 1024x768 screen. Text adventures in the 1980's used larger type and occupied the entire screen, but that was all they had. Large type is easier to read, and it seems silly for text IF to default to a small font.

3.         I expected to be able to use my mouse. The older games didn't allow this, but surprisingly many new IF titles do.

4.         Early text adventure games required that the user create a paper map of the environment. Creating a map, in my opinion, is drudgery. Early games couldn't create a map because they didn't have graphics, but I expected the modern IF titles to have automatic mapping features. I didn't find any.

5.         As a said earlier, the parser and depth of the worlds impressed me, although I still ran into "guess the verb" problems that my memory had neglected to recall with its nostalgia.

6.         The new IF was much more experimental (and intellectually interesting) than the older commercial work. I suspect this is because IF has been taken over by intellectual hobbyists who don't cater to the masses.

These are just my observations. Many people enjoy playing text IF.

The larger question remains... Why aren't text adventures commercially viable? Here are some hypothesis:

Reason: Shrinking market for adventure games

While the number of computer users has increased by several orders of magnitude since the early 1980's (when text adventures were popular), the market share for adventure games has gone down. They are now a minor genre (only 1%-2% of the market), and are dwarfed by the market for first-person shooters, sports games, role playing games, and the sims. Occasional successes, like Myst, do arise, but the category does not dominate like it used to.

Why is this?

1.         Only certain personality types are attracted to adventure games, namely those people that like to solve problems and "explore" new ideas. In the early 1980's, people that purchased computers were oddballs, spending a thousand (or more) dollars on very expensive calculator. I suspect that the same personality traits that attracted the early-adopter computer users were also the same traits that attract people to adventure games.

Nowadays half the population owns the computers. While the "oddballs" still exist, they're a smaller percentage of the computing population.

2.         In the early 1980's, there were half a dozen major operating systems and no standardisation for graphics (if the computer even had graphics). Text adventure games had the advantage in this environment because they were easy to port to different operating systems; graphics-based software, on the other hand, was difficult to impossible to port. A text adventure therefore worked on more platforms and had a much larger market share, making them more profitable.

3.         Many computers did not have graphics capabilities at all. Game players were left will little other choice than text adventure games.

Because adventure games only appeal to a small segment of the overall population, they will only ever be a niche market. First-person shooters, sports games, and others less intellectual games will dominate.

Reason: Old technology

As I said earlier, running a text adventure on a computer that's capable of fancy 3D graphics, sound, and Internet communications feels like a waste. Having said that, I still occasionally watch black-and-white movies on my colour television. User's feeling like they're wasting technology is probably only a small part of the text-adventure's demise.

I suspect that a lot of what drives games sales is new technology. People like games that use the latest and greatest technology. If you look at the history of games you'll see that those games that use the latest technology at the knee of the technology adoption curve are major successes. If a game uses a technology before it's widely available, such as Ultima IX's use of 3D, it will fail. If it waits until the technology is established, another game will have become wildly successful instead.

Here are some examples:

Text adventures are not commercially viable any more not just because they fail to use 3D graphics, sound, and CD-ROMs, but because they don't use the latest and greatest technology. (This is also a strength, because using the latest technology increases development costs, which would change the nature of text IF.)

Following this line of thought though, one can predict that the next big gaming hits will use the next big technologies:

Adventure games could take advantage of most of these future technologies. Luckily, only the 3D modelling aspects are development syncs. Some technologies, such as DVDs and speech recognition, are particularly useful to an adventure game genre. (Interestingly, speech recognition is less useful for other genres, although one first-person shooter has tried to incorporate it.)

Reason: People aren't willing to pay for text adventures

Even if people are willing to play a text adventure, they aren't wiling to pay for it. There are a few reasons for this:

1.         Text adventures are obviously old technology, so they're not worth as much to people. DVDs of black-and-white classics sell for less than modern blockbusters. Even the distribution medium matters: A DVD movie sells more than a video tape of the movie (old technology), which sells for more than the paperback book of the movie (older technology). Interestingly, the DVD is the cheapest media to manufacture.

2.         People often value objects by size. Have you ever noticed how software is sold in large boxes that contain nothing but air, a registration card, and a CD-case? Text adventures are "small". Thousands of them can be fit onto a single CD-ROM. Contemporary graphical adventure games take at least four CD-ROMs. One that required four DVDs would be even more "valuable". Because text adventures are so small they must not be worth as much money as a graphical adventure that requires four CD-ROMs.

3.         Because of their small size, text adventures are distributed on the Internet. When someone "buys" software over the Internet, they hand over their credit card number (which people don't like doing over the Internet) and receives a E-mail with a password. That's it. No box, no CD-ROM, no map, no peril sensitive sunglasses, nothing. People like to receive tangible objects for their purchases.

4.         Why pay money for a text adventure when so many free ones (many of them good) can be downloaded from the web?

Reason: Others

Some other factors also hurt text adventures:

Conclusion

As you already knew at the beginning, text adventure games are not commercially viable. My examination of the problem merely enforces the conviction.

However, that doesn't mean that that interactive fiction isn't viable. It does need to change, however, if it's going to be anything more than freeware:

 


 

Virtual worlds and virtual holidays

(Or... What I did on my holiday)

(Back to TOC)

25 June 2004

by Mike Rozak

I spent the last few weeks acting as tour guide for relatives from the US, showing them around northern Australia. In my spare time (the few wakeful moments that I lay in bed before I fell asleep) I puzzled about the nature of virtual worlds and how users might like to experience them. Naturally, as I drifted off to sleep, I combined the two thoughts and considered the similarities between holidays and virtual worlds.

Quite a few similarities exist. After all, virtual worlds are places that people "escape" to, and holidays are trips to exotic places, usually an attempt to escape from the stresses and dullness of ordinary life. Of course, a virtual world is not entirely the same as a holiday in an exotic location, but I'll get to those issues later.

By the way, don't take this write-up too seriously.

Let me enumerate the "elements" of a holiday and how they are similar to a virtual world:

Reasons for going on a holiday:

How people get to their destination:

What tourists do on the holiday:

Holidays also include some experiences that aren't so easily classified:

Of course, virtual worlds differ from real-world holidays in a number of important aspects:

Does this comparison amount to anything? Maybe. Maybe not. It does provide a different perspective though, one that I wouldn't take too seriously.

 


 

The attraction of impossibility

(Back to TOC)

6 July 2004

by Mike Rozak

This morning I awoke with another "angle" on virtual worlds spinning through my head. It's an obvious one that any Hollywood script-writer would know, but it took some time for me to get my brain around to it...

People play virtual worlds to do or experience things they can't in real life.

The reasoning for this is fairly simple: If they could experience it (whatever that may be) in real life, they would; real life provides a more rewarding experience. Virtual worlds are merely a backup mechanism for experiences that can't be had in reality.

With this thought in mind, I started listing off the impossible things that players in contemporary MMORPGs can do or experience:

Interestingly, most of the "impossible" things that MMORPGs allow their players to do are actually possible in the real world. They just require the person to be an adult with lots of money, and in the case of illegal acts, an adult with enough money to hire a very good lawyer. According to this list, the demographic most attracted to MMORPGs would be teenagers, or adults without much money. Real-world statistics say it's about half teenagers and half 20-somethings.

My list is incomplete though. Virtual worlds (not just contemporary MMORPGs) allow players to do or experience other impossible activities. MMORPGs haven't catered to these activities because their designers haven't thought of them (unlikely), or because the demographic is too small.

Of course, both my lists are incomplete. You should be able to add dozens more impossibilities.

 


 

Virtual world spectrum

(Back to TOC)

20 August 2004

Revised 7 September 2004

by Mike Rozak

For awhile I've been pondering the spectrum of story-like entertainments that can be produced on a computer. The most obvious story-like entertainment is, of course, a story. Computers can display stories as either E-books or videos. Neither of these take full advantage of the computer's abilities since they don't support interactivity (the ability for players to affect the outcome of the story) or multiple users (using the Internet to place several players within the story). Adding both interactivity and multiple users to a story turns it into a completely different beast: a virtual world.

What lies in between a story and a virtual world?

To better understand what happens as each technology is added, I decided to do a thought experiment, beginning with a story, and adding interactivity and multiple users in different steps. After adding the technologies, I pause and see what's happened to the entertainment. Each technology changes the experience, with obvious improvements as well as newly introduced problems, which I have enumerated for each step.

Step 0: A linear narrative (stories) vs. the real world (0 players)

I'll begin with a novel or movie, a form which everyone is familiar with. Novels and movies often take place in imaginary worlds, following a handful of characters (controlled by the author) through the world. The world, characters, and their actions are designed by the author to maximise the entertainment value. Most people wouldn't call novels or movies virtual worlds though.

Both novels and movies can be displayed in a computer, novels turning into E-books, and movies into video. (Note: If the movie's computer graphics are generated real time and played from within a game, the movie is called a cut-scene.)

My first transition is from the real world to a story. Why would anyone give up the real world to experience a story?

Advantages of a story (compared to the real world)

Disadvantages of story (compared to the real world)

  • The story allows the reader to undertake (imagined) experiences that would be too dangerous in the real world.
  • Stories let readers have (imagined) experiences that would be very expensive in the real world.
  • Stories let readers have (imagined) experiences that would be impossible in the real world. They don't even need to take place in the real world.
  • Stories allow the reader to have (imagined) experiences through other people's eyes.
  • An author can chose the story's setting, characters, and actions, allowing for fictional stories that would rarely occur in real life. Coincidence doesn't exist in a story.
  • The reader can chose which story they want to read/view out of millions of stories... Conversely, there is only one real world. Additionally, they can stop reading a story, re-read it, or even skip ahead, none of which are possible in the real world.
  • One of the author's goals when writing a story is to provide an enjoyable experience for the reader, barring a few morals thrown in. Consequently, stories are relatively safe, compared to the real world where people's interests often conflict with one another.
  • A story can be built around a single objective for the characters to complete. The story's world is usually tailored to facilitate this objective.
  • Stories have an ending, which makes them psychologically satisfying.
  • Time and space can be bent and broken.
  • Readers don't have to think (much) while reading a story.
  • A story, especially in book form, can be experienced no matter where the reader is in the real world.
  • Good size for loners.
  • The reader has no control of what happens the story, other than the ability to stop reading/viewing. (Reducing immersion.)
  • Stories are not as sensorally realistic (no 360-degree view, no smell, no touch, etc.) as the real world. (Reducing immersion.)
  • Reading the story has no impact on the characters in the story, or (for the most part) on the real world.
  • Corollary: Because of the lack of control and limited sensory realism, stories are not as immersive as the real world.
  • Stories have an ending, which limits their duration.

Step 0.5: A rail-game vs. a linear narrative (1/2 of a player)

In a "rail game" the player can affect the narrative in very limited ways. Usually this is a choice of camera location, which character will be followed, optional sub-stories, and the occasional "make the right decision or the story ends here." Some rail games allow for a handful of story endings. Adventure games are often rail games. The Dragon Slayer laser-disc game from the 1980's was a rail game. Choose-your-own-adventure(tm) books are rail games.

Advantages of a rail game (compared to a story)

Disadvantages of a rail game (compared to a story)

  • Choices force/encourage users to think (as opposed to vegetate)... some people like this.
  • Interactive entertainment is more dangerous than a linear narrative since if something bad happens it could be the player's fault. Some players like the danger. (Danger increases with the amount of interactivity and number of players.)
  • Because of the ability for users to follow different characters and experience optional sub-stories, they can (somewhat) tailor the experience to their desires.
  • Choices encourage immersion. (Although rail games don't provide many choices, so their immersion is limited.)
  • Players know that their choices are limited so they don't get as frustrated when they can't do what they want. (See disadvantages of an interactive experience.)
  • Replayability - The game can be played a second time and the story produced will differ... slightly. Part of the entertainment of the format is the ability to see "What would happen if...".
  • Choices force/encourage users to think (as opposed to vegetate)... some people don't like this.
  • Interactive entertainment is more dangerous than a linear narrative since if something bad happens it could be the player's fault. Some players dislike the danger. (Danger increases with the amount of interactivity and number of players.)
  • The author must take care that any particular branch of the rail doesn't ruin the story for the player.
  • The player quickly learns that they can make a choice, and then backtrack if it's not one the like. In Choose-your-own-adventure(tm) books players will return to a previous page, while in games they load a saved game. (This is reduces immersion.)
  • An interactive experience requires a computer to be nearby; computers are relatively expensive. (Although the classic Choose-your-own-adventure(tm) books made an attempt without a computer.)

Step 1: Adding single-player interactivity to a rail game

Adding more interactivity to a rail game allows the player to take full control of a character in the author's world, instead of just occasional control. Of course, due to technological and simulated limitations, players never have complete control of their characters.

Most "computer games" fall under this category.

Advantages of interactivity

Disadvantages of interactivity

  • If an outcome is predictable, choices allow the user to tailor their experience to their mood. (Example: If a PC wanders into a dungeon, they know they will meet monsters and be able to take out their aggressions. If they stay in town for the day they know they will be safe and can go shopping.)
  • Interactive worlds allow users to experience the ramifications of their choices in relative safety. (Example: Grand Theft Auto lets people become car thieves without risking the jail sentences. Some games allow the player a choice between being good or evil, and tailor the game accordingly.)
  • Choices allow the player to overcome obstacles or change the world, which provides a sense of accomplishment that can't be gotten from stories. (Note: This feeling is magnified in a world where other players experience the same world.)
  • Linear narratives (stories) and rail games are completely controllable, allowing authors to build plots that are elegant, perfectly paced, and highly enjoyable, but which would collapse if a player were allowed to interact with the world in significant ways. Stories can still exist in an interactive environment, but they area a pale shadow of what can be produced in non-interactive fiction. The more story that the author imposes, the less interaction there is.
  • If the players aren't given as many choices as they think they should have, or if the UI to select the choice is obtuse, the players may get annoyed and stop playing.
  • If a choice results in both a negative and illogical outcome, players may get annoyed and stop playing. (A similar effect holds true for stories, but it is more pronounced in an interactive entertainment since inconsistencies can't be explained away as a stupid action on the part of the author-controlled character.)
  • Corollary: The author cannot produce a system that will allow users to do everything they wish to. An interactive system will always provide some frustration caused by the lack of character control.
  • Adding interactivity makes content creation, including as graphics and animation, more difficult and expensive. An animated movie is expensive; an interactive fiction with animation as good as an animated movie is impossible.
  • Time must be linear, although not necessarily constant... The author cannot know the future in an interactive world.

Step 1b: Adding a sustained Internet connection

To produce a multi-player game, we need to add an internet connection to a single player game. Single-player Internet games do not exist (except in rare circumstances) because players don't get any benefit from the combination. Therefore, the configuration is unstable.

However, separating the addition of Internet from adding more players makes things a bit clearer.

Advantages of a sustained Internet connection

Disadvantages of a sustained Internet connection

  • The author can watch players play, and fine-tune the experience better. (Although a large beta would allow for this too.)
  • Players can "download" expansions without having to visit the store, or even consciously install them. (They don't need to be connected to the Internet all the time to do this.)
  • Staggered downloads allow the players begin play without downloading the entire game. (Could be done without a sustained connection, but the connection helps.)
  • The author can change the world on-the-fly without the player knowing. (Again, this doesn't really require a sustained connection, although the connection helps.)
  • Corollary: The author (or one of his minions) can fill in for poor computer AI, or perhaps create new content on the spot.
  • Allows multiple players to play the same game. (See below.)
  • Significantly reduced piracy... which is good for the author, at least.
  • Internet connections are slow and unreliable.
  • Internet connections are expensive to maintain, and the software is more expensive to develop.

Step 2: Supporting 10 players in one world

The next step in the story-to-VW spectrum is to support a small group of players (approximately 10) in a world. Being such a small group, they'll know one another well, probably in the real world too.

Neverwinter Nights is a CRPG that supports small groups of players. Face-to-face RPGs like Dungeons & Dragons are also based upon a small number of players.

Advantages of 10 players

Disadvantages of 10 players

  • Bleedthrough: Interactions with PCs (directly or indirectly) are more meaningful because real people are being affected on the other side. Making a PC happy is different than making a NPC happy.
  • Players choose their own character, instead of the author choosing the player's character to fit the story.
  • Can play with friends (or strangers).
  • Strangers (or friends) make more clever opponents than does computer AI. (This extra challenge improves immersion.)
  • Players can experience other facets of people as they play in the world... just as people's personalities differ when they're on holiday vs. at work.
  • Players begin using the world as a place to meet and socialise, not necessarily play.
  • Sub-games, like chess or capture the flag, are possible, although they are much better handled in worlds with more players.
  • Good size for a group of friends to get together and play.
  • A storyteller (classic D&D dungeon master) can provide a personalised touch to the experience.
  • Bleedthrough: Interactions with PCs (directly or indirectly) are more meaningful because real people are being affected on the other side. Killing a PC is different than killing a NPC.
  • If the players can all connect at the same time every week, the world can change as the players progress through the game. However, if players come and go then having the world change based on the player's actions becomes much more difficult. (The second case is more likely.)
  • A story is much more difficult to handle, not only because the players may go in different directions, but cut-scenes (important for conveying story) are awkward with so many people playing. (For example: What if one of the players isn't in the right place to see the cut-scene.)
  • 10 players consume content more quickly than 1 player.
  • While a PC can use a spell/skill to read the minds of NPCs, he/she can't read the minds of other users.
  • The flow of time must be constant, unless all the players agree to the change.
  • Due to Internet transmission lag, twitch games with multiple players are not possible.

10-player games can be designed so they can be experienced in single-player mode, although the content design is then torn between a single-player and a team-oriented experience.

Step 3: Supporting 1000 players in one world

If the number of players is increased to 1000, the dynamics of the game changes dramatically. The world changes from a cozy outing with friends into a world populated by mostly strangers, some of them enemies.

Advantages of 1000's of players

Disadvantages 1000's of players

  • A world filled with 1000's of players is much less predictable (and in many ways more interesting) than one filled with only a few players.
  • 1000-player virtual worlds tend not to have an official ending, although players eventually get bored and leave.
  • Players (alpha-(fe)males) can try to gain power over other players.
  • Rather than limited the player to a single objective, they are given a range of objectives: Such as be the strongest, wealthiest, most-knowledgable, etc. Objectives competing against other players tend to work the best. The objects are more tailored to fit the world than vice versa.
  • Meet new people.
  • The experience becomes a long-term one (sometimes years) where players get to know and participate in the community.
  • Players join large groups (guilds) that often compete against one another.
  • Players interacting with one another provide much of their own entertainment. The author's spends a lot of time managing the rules that allow the players to interact.
  • To some players, changing the world is an important achievement because the changes affect 1000's of other players.
  • Anonymity is possible.
  • Corollary: Players can "try on" different personalities and see how other players react to the new personalities. (This gets back to choices available in an interactive world.)
  • Players can sit back and watch others play. (Are we back to a linear narrative in this case?)
  • The experience is large enough and long enough that sub-games fit in well.
  • Sub-games that require 1000's of players are now possible. These include economics, politics, and warfare.
  • Because there are so many players acting as opponents to one another, good NPC AI isn't as necessary.
  • Virtual worlds targeted at special interest groups (like role players) will be about this size (100 - 10K simultaneous players).
  • A world filled with 1000's of players is much less predictable then one filled with only a few players.
  • 1000-player virtual worlds tend not to have an official ending, although players eventually get bored and leave.
  • Players (alpha-(fe)males) can try to gain power over other players.
  • The world is too large for a single objective.
  • Other players cannot be controlled by the author; as a result they often bring the real world into the virtual one, and ruin the immersion for other players.
  • While the author's goal is to provide every player with an enjoyable experience, players have their own motivations, often conflicting with other players' enjoyment.
  • Virtual worlds with 1000's of players can't be started, stopped, paused, or be rewound by the player.
  • Corollary: Virtual worlds with 1000's of players are more dangerous than a world with a few players. (For all the above reasons.)
  • With 1000's of players, only a few of them can be the hero. An exponential curve for wealth distribution, power, etc. develops.
  • Corollary: Only a handful of players will be powerful enough to change the world. The rest must accept what they're given or work as a group.
  • Providing a handful of storytellers whose job it is to add a personalise touch to the world is possible, but made difficult by the likelihood of them getting in each others way.
  • The world must be larger so people don't feel crowded together.
  • If there's a bug, a player will find and exploit it at the expense of other players, making testing more critical (and expensive).
  • The combined brainpower of 1000's of players is much larger than the authoring team's handful of brains.
  • Small businesses use exploits (or low-cost employees) to play the boring parts of the game for players. (Example: Everquest items and characters sold on E-Bay.)
  • The flow of time is constant.

1000-player virtual worlds can include design elements intended for single-player or team-playing, but these are never as strong as a design specifically for single-player or team-playing.

However, with "private regions" and other insulating techniques, it is possible to make 10-player worlds, single-player worlds, and pure stories within the larger 1000-player virtual world. At that point, does the large virtual world become merely a way to access the private regions?

Step 4: Supporting 1,000,000 players in one world

Increasing the number of players to 1,000,000 produces further ramifications, only a few of which can be guessed, since no virtual world is even close to 1M simultaneous players. This section is almost pure speculation.

Advantages of 1M of players

Disadvantages 1M of players

  • Large VW's will be the domain of the largest corporations.
  • Players provide their own content. The better content comes from business that exist selling products/services in the virtual world for real-world money. (For example: A business might have a host-a-murder service they sell that involves a group of VW players in a murder mystery.)
  • The VW authors are no longer interested in content, but in providing a platform that allows the smaller businesses to operate and thrive in their virtual world.
  • The virtual world physics-layer is genre independent.
  • Corollary: Several genres are bundled into one product, just like cable channels come bundled together.
  • Economy, politics, and warfare should work best in large worlds.
  • The cycle goes full circle. While a different world is created, a virtual world populated with 1M players has some of the same systemic problems as the real world. As a result, the larger virtual world needs smaller sub-worlds, geared towards 1, 10, and 1000 players.
  • While it's possible to have sub-worlds geared towards 1, 10, or 1000 players, their scope is (usually) limited by the physics of the 1M player world, and their effects cannot contradict what's happening in the 1M player world.
  • Only a handful of 1M+ virtual world platforms will exist.
  • The virtual world must be huge.
  • Role playing is non-existent. The real world seeps in at all points.
  • Do 1M-player virtual worlds have wars? Will real-world conflicts (such as Israel vs. Palestinians) filter into virtual worlds?
  • A virtual legal system is needed in the virtual world to arbitrate disputes.
  • Storytellers need to work on a large scale, personalising experiences for guilds rather than individual players. Alternatively, the storytellers need to isolate individual players in sub-worlds.

Sub-worlds

Because a world designed for N players has flaws, designers will inevitably design in sub-worlds to handle smaller groups of players. These sub-worlds can take two forms: Those weakly separated from the main world, and those strongly separated.

A weakly separated sub-world allows uninvited players to enter. Quests, for example, are weakly separated sub-worlds since the quest is a "sub-world" targeted at 1-10 players; only they can "complete" the quest. However, the quest takes place in the same world as all the other players.

A strongly separated sub-world only allows invited players to enter. Private regions/dungeons provide for this. 1-10 players get together and have a private area of the world create. Only they can enter it. Once there, they are separate from the rest of the world.

Conclusion

The transition from a linear narrative to a full-blown virtual world is an interesting one. Each stage of the cycle is different enough from the others to allow for a unique experience.

I have made a small Microsoft Excel spreadsheet that graphically displays what each "step" is best at handling. It's in VirtualWorldSpectrum.xls. Of course, the numbers are only guesstimates. The important information in the graphs are the trends; some world sizes are better for some types of experience.

 

 


 

Virtual world as platform

(Back to TOC)

5 September 2004

by Mike Rozak

In one of my previous articles I mentioned that I thought of virtual worlds as platforms. I thought I'd go into more detail:

Imagine creating a virtual world... some place with lush scenery that your character can walk around in. You spend all your time walking around looking at the picturesque trees and impossibly tall mountains. And when you get bored of that, your character finds other places to wander around in. And when you get bored of that?

Might as well at chat... You are no longer wandering around the world alone. You can see other people in the world and talk/type to them. You can even animate your character with emotes. This will keep you occupied for a bit longer, but eventually you will get bored. Now what?

How about providing some games to play with other people? Maybe allow people to fight, fly planes, or play virtual capture the flag... Will this keep you interested? Yes, for awhile, but even this will get boring.

Continue ad infinitum...

Virtual worlds by themselves quickly get boring. To make them interesting developers add sub-games and activities to them. The world is continually changing and growing (in size and number of sub-games and activities) to prevent players from getting bored and leaving.

Contemporary virtual worlds are almost all based off one major sub-game, the CRPG (like Morrowind or Neverwinter Nights). A few are based upon vehicle simulations, such as space combat. One, Star Wars Galaxies, even combines both into the same world. I predict that over time more and more virtual worlds will be based off several sub-games, just as Star Wars Galaxies is.

What sub-games can a virtual world include?

Single-player sub-games

Here are some single-player sub-games that can be added to a virtual world: (Note: Single-player sub-games are not ideal virtual world material because they don't encourage people to play together, see "What makes a good sub-game?")

Sub-games for one to a few players

Some sub-games can either be played alone, or with a small group:

Sub-games for a few players

Some sub-games require a small group of people:

Sub-games for many players

Some sub-games require a large group:

Meta-game

The act of creating a virtual world is also a sub-game:

Other sub-games?

While I listed quite a few sub-games, the list is certainly not complete. With a bit of imagination you can probably double their size.

What makes a good sub-game?

So what makes a good sub-game? I'm not 100% sure, but here are some general guidelines:

1.         It should be an activity that can be done on a computer.

2.         Most virtual world sub-games are inferior to their stand-alone counterparts. For a sub-game to work well in a virtual world, it should get some benefit from being multiple-player or integrated with other sub-games.

Example: MMORPGs are based on CRPGs, but they make rather poor CRPGs. The reason they are successful is because MMORPGs provide a multiple-player environment and synergies with other sub-games (such as economics).

3.         The sub-game should be resistant to cheating. One of the problems with adventure games and trivia contents (listed above) is that they are easy to cheat on. If a player cheats on a single-player game they are only hurting themselves, but virtual worlds turn into competitions (for some people), so if they cheat in a virtual world, they get an advantage over other players.

4.         Small sub-games that can't be sold on their own, but which can be sold as part of a larger package.

Example: Chat is a free commodity on the Internet. However, most people spend significant amounts of their time in a virtual world using (and paying for) the VW's chat.

Business reasons for sub-games

Why would a business want to bundle a number of sub-games into a virtual world? Why not sell them separately?

1.         The whole is more valuable than the sum of its parts. (See "What makes a good subgame?")

2.         Players would rather pay one large monthly bill than many small ones. As a result, virtual worlds that offer more sub-games are more likely to acquire and retain customers. (Think of cable TV... you only have one cable TV provider, and only pay one bill for all 50+ channels.)

3.         One retail package for a host of games: Retail shelf space is expensive and difficult to get. Bundling a number of games into one box reduces the profit per box, but potentially increases subscriptions (which is where virtual worlds make their money).

4.         Brand names and ease-of-acquisition - If the customer wishes to play a genre of game, such as a racing game, they are more likely to purchase the game from a company they are familiar with. Furthermore, if the game is already part of their virtual world, they are less likely to buy a competitor's version.

Conclusion

Don't think of a virtual world as a single game. Think of it as a platform that can support many games and other activities.

 

 


 

The really big VW problems

(Back to TOC)

19 September 2004

by Mike Rozak

I have read many a discussion on various newsgroups about issues in virtual worlds, such as class-based systems vs. skill-based systems, the best combat system, and what to do about spawning problems.

For whatever reason, most of the discussed problems don't impress me. From my POV, virtual worlds have a handful of large problems that dwarf the classed vs. classless (and ilk) discussions. These large problems are so huge that they may be unsolvable, or only solvable to the exclusion of other solutions.

Following are the major problems with virtual worlds, as I see them (in no particular order):

Are there solutions to these problems? Maybe. Below are listed some existing solutions.

How MMORPGs try to solve these problems

Here are some of the techniques that MMORPGs use to solve these problems.

Sensory realism

Up to half the MMORPG team is tasked with producing 3D models, textures, animations, sound recordings, and music.

User input

Rely on the mouse or joystick with occasional keyboard use (mostly as discrete buttons like AWSD).

Players

Expel cheaters and griefers. Make some activities, like player killing, impossible.

NPC AI

Generally ignored.

Plot maintenance

Generally ignored, although a few MMORPGs try to maintain a world plot.

Personalization of content

Generally ignored.

The static nature of the world

Generally ignored.

Perplexity

Keep the perplexity as low as possible since the user input and mass-market focus cannot handle anything more complex.

Cost of content generation

Expensive content is actually good for the large MMORPGs since it prevents competition.

Similar to reality

Mass market means that reality is king. Most contemporary MMORPGs occur in a fantasy or science fiction setting; this is in part because MMORPGs are not mass market yet, and even the fantasy/sci-fi settings are based on the real world with a genre veneer. (Example: Why do laser guns always look like pistols or machine guns? Why do aliens look like humans?)

The network

Some motion prediction algorithms.

How text MUDs try to solve these problems

Sensory realism

Generally ignored. To a text MUD player and author, text is superior to everything.

User input

Keyboard used to enter English sentences.

Players

Like MMORPGs, except that many MUDs encourage role playing.

NPC AI

Generally ignored.

Plot maintenance

Generally ignored, although a few MUDs maintain a world plot.

Personalization of content

Generally ignored.

The static nature of the world

Generally ignored.

Perplexity

English sentences allow for a much higher perplexity than MMORPGs, although perplexity could still be improved.

Cost of content generation

MUD content is very cheap to produce.

Similar to reality

MUDs are more likely to veer away from commonly accepted genres.

The network

Generally ignored since it's not really an issue, the network being fast and reliable enough for text.

How CRPGs and Adventure games try to solve these problems

And you thought I was only discussing about virtual worlds... Single-player CRPGs and adventure games have many of the same issues.

Sensory realism

Just like MMORPGs.

User input

Just like MMORPGs.

Players

Not an issue.

NPC AI

Because there aren't any other players, NPC AI is more important. NPC combatants are often more intelligent than their MMORPG equivalents. NPC conversations are often better too.

Plot maintenance

The plot is handled as a "string of pearls" where the user can wander around freely in the current "pearl" until they advance the plot and are moved to a new "pearl". Some plots include branches.

Personalization of content

Generally ignored.

The static nature of the world

As the plot advances, the world changes along with it.

Perplexity

Just like MMORPGs.

Cost of content generation

Just like MMORPGs.

Similar to reality

Just like MMORPGs.

The network

Not an issue.

How face-to-face RPGs try to solve these problems

And you thought I was only talking about computer games? Traditional face-to-face RPGs (like Dungeons & Dragons) encounter these same problems.

Sensory realism

Mostly imagination derived from speech. Maps, miniatures, and occasional sketches are also used.

User input

Players communicate their actions verbally to the GM, occasionally using hand gestures.

Players

Because only a small group of players are in a session, and they all know one another, cheating and griefing are fairly rare. The players (as a group) control the amount of role playing expected.

NPC AI

The GM supplies the AI.

Plot maintenance

The GM and players work together to produce a plot.

Personalization of content

The GM personalises the content for the players.

The static nature of the world

The GM changes the world based upon the players actions and the needs of the plot.

Perplexity

The GM handles the world simulation.

Cost of content generation

The GM produces the content, often on the fly, making it very cheap.

Similar to reality

This varies with the group, with some groups veering further away from reality than others.

The network

Not an issue.

How stories (TV, movies, and books) try to solve these problems

And you thought I was only talking about games? TV shows, movies, and books have many of the same issues.

Sensory realism

Books rely on the imagination for sensory realism. TV shows and movies have huge budgets devoted to sensory realism.

User input

Not an issue.

Players

Not an issue.

NPC AI

NPC AI is "baked" into linear narrative.

Plot maintenance

Plot is "baked" into the linear narrative.

Personalization of content

Since there are so many stories available, if one doesn't suit a viewer can always find another.

The static nature of the world

World changes are "baked" into the linear narrative.

Perplexity

Not an issue.

Cost of content generation

Books are cheap to produce. Movies are very expensive to produce, although their costs are dropping with technology.

Similar to reality

Mass-market TV and books are based on reality. Books, which are less mass-market, can escape some of the usual chains.

The network

TV has a network specially designed for it.

How the Holodeck tries to solve these problems

You thought I'd stick to reality?

Sensory realism

Artificial matter.

User input

Players exist within artificial matter, so there is no extra user input. (Although this limits their input.)

Players

Only guests and parasitic aliens are allowed in.

NPC AI

Great AI.

Plot maintenance

Not really specified, but I assume the great AI has something to do with it.

Personalization of content

Not really specified, but I assume the great AI has something to do with it.

The static nature of the world

Not really specified, but I assume the great AI has something to do with it.

Perplexity

Not really specified, but I assume the great AI has something to do with it.

Cost of content generation

Not really specified, but I assume the great AI has something to do with it.

Similar to reality

Players in the holodeck can't even take on a different appearance.

The network

Holodecks don't seem to be tied to a network.

Interrelated problems

The act of solving one of the major problems often causes the other problems to become more difficult... for example: Increasing sensory realism makes network speed and reliability that much more important, since players are likely to notice lag and dropouts.

Below is a list describing how solving one problem makes some of the others more difficult to solve:

Sensory realism

Increased sensory realism makes...

  • User input becomes more important, so, for example, players have better control of the emotions that their players display.
  • NPC AI more difficult because the animations/sounds will be good enough to convey emotions and subtle movements.
  • Cost of content generation increases.
  • Shows off problems in the network delay, throughput, and reliability.

User input

Increased user input makes...

  • Network bandwidth higher.

Players

Solving player undesirable conflicts makes...

  • NPC AI more important because the players spend less time fighting amongst themselves.

NPC AI

Increased NPC AI makes...

  • Plot maintenance more difficult because NPCs want to choose their own directions. Does a NPC move the plot forward or act like it's personality dictates?
  • Personalization of content is harder because NPCs won't necessarily want to do what the player wants them to do.
  • Cost of content generation increases.

Plot maintenance

Improving plot maintenance makes...

  • Perplexity is more difficult to achieve because a good plot often requires a low perplexity.

Personalization of content

Improving content personalization makes...

  • Conflicts between players more difficult to solve, because even if the players aren't trying to conflict with one another, their personalization-goals might.

The static nature of the world

Making the world more dynamic causes...

  • NPC AI is more difficult since NPCs must cope with the changing world.
  • The cost of content generation increases.

Perplexity

A higher perplexity makes...

  • Sensory realism more difficult to achieve. (TV/movies, with no perplexity, have might higher sensory realism).
  • User input more difficult since the player has more options.
  • NPC AI more difficult since it must respond to more conditions.
  • Maintaining a plot is trickier since players have more ways to derail it.
  • The cost of content generation rises.
  • The world must resemble reality more. (Early video-games like Pac-Man were further from reality than today's games.)

Cost of content generation

Decreasing the cost of content makes...

  • Since the solution to every problem almost always leads to an increase in costs, reducing content generation costs negatively affects the solutions to the other problems.

Similar to reality

Diverging reality causes...

  • Sensory realism is more difficult... if your PC has a tail, how do you feel it?
  • User input is more difficult... if your PC has a tail, how do you move it?
  • Effectively increases the cost of content generation because the market is smaller.

The network

Improving the network causes...

  • No problems that I can see.

Conclusion

A VW can't solve all the major problems since the act of solving some problems causes others to get worse. Most MMORPGs have decided that sensory realism is the most important problem, so they tackle that to the detriment of the other problems.

 

 


 

Stop the buffet

(Back to TOC)

31 October 2004

by Mike Rozak

In, my last whitepaper, "Virtual World as Platform", I discussed why I thought virtual worlds could be more than just multi-player CRPGs, as they so often are today. Practically any game or fun activity that can be played on a computer can be incorporated into a virtual world. The most obvious of these are: CRPG, chat, vehicle simulations, economy games (like Lemonade), puzzles, and card games.

Some virtual worlds do include many of the sub-games and activities that I mentioned. However, they provide them to the player as a kind of buffet, letting the player kill 10,000 orcs until they're utterly bored with the task, then shift to cutting down 10,000 trees for wood to make boats, and then become obsessed with ruling the high seas.

I don't think providing a buffet is the best way for a VW to offer its sub-games. This makes it nothing more than a games-galore web site like Yahoo! games, or a chat site like Yahoo! chat. Of course, Yahoo's services are free to players, which makes them that much harder to compete against. Yahoo's games don't synergise, like I mentioned in "Virtual World as Platform", but they're free.

So how can a VW author package up valueless sub-games and activities, and produce something that's valuable and unique, not a Yahoo! games/chat with orcs?

The key is timing. Timing is everything...

Music theory and virtual worlds

The sub-games (and activities) offered by a virtual world are like the keys on a piano.

Most virtual worlds are so pleased they have 88 keys (well, most only have 4 or 5 keys) that they plop the player in front of the player, teach them how to strike a key, and then let the player "go to town" and have fun.

Players do have fun, for awhile; they press C, and then C#, get bored of that and hit the low G, or maybe even the highest A. They don't produce music though; the create a cacophony of notes which sounds utterly horrible unless you're into atonal music. The players know this, so they get bored and give up.

However, if you place a trained composer or improvisationist at the keyboard you'd never believe it's the same music. Even making up music on the spot, the composer/improvisationist produces music that's infinitely sweeting than the bangings of most players. Those players that had just made a cacophony the minute before look on amazed.

How come the composer and improvisationist is better at the piano? For one, they spend their whole life sitting at the keyboard... but then again, so do many players. A professional also knows music theory, and is well aware at a conscious and subconscious level which keys should follow one another, and which sound good in combination. The average person is not.

Some improvisationists, called Jazz players, use another trick; they memorise a series of chord progressions for a song, but not the individual notes. Then, when they're playing the piece, so long as they stay (mostly) within the chord, it sounds good. Composers often use the same trick, although they may not admit it. I'll come back to chord progressions later.

To sum it up, on a piano, timing is everything. Hitting the right notes at the right time produces a much more enjoyable experience (for the player and listener) than playing notes willy nilly. The same holds true for VWs, I suspect, but you'll take more convincing.

Literary theory

When an author writes a novel, he has several common constructs to play with: characters, setting, action, conflict, backstory, plot, and theme. I'll add one more that is so obvious it is rarely discussed, timing.

The seldom mentioned, "when", is very important. I'll illustrate with a thought experiment... Think of your favourite book. As you mentally skim through it you'll notice the author first introduces the main character, then the place where the character is, perhaps some backstory, and then some action. Soon, another character is added, with some more action of backstory, following by a bit more setting.

Now, imagine if the author reordered the book so that all of the backstory came in the first part of the book, followed by all of the setting descriptions, followed by an introduction to all the characters, and finished by a description of all the action.

The book would be awful. If you don't believe me, read the Silmarillion, a compilation of Tolkien's backstory and place descriptions. A portion of the Silmarillion's backstory and places are included in the LOTR, but they're spread throughout the book and are much more palatable in moderation.

I haven't even mentioned what ordering within each of the categories (backstory, location, characters, and action) does to the story. Tolkien explains the backstory of the one ring at the beginning of LOTR so readers know how important it is; pushing the backstory to the moment after Frodo tosses it into Mount Doom would change the flavour of the story entirely. The same goes for when locations and characters are introduced. And of course, action that doesn't follow cause-and-effect, or which isn't sequential, cant be very disturbing.

Virtual world theory

Virtual worlds have many of the same elements as a story:

Hopefully you are at least considering the fact that stories and VWs have many similar constructs.

Yet more analogies...

Virtual worlds are not like stories though. If an author tries to force players to do something, or puts words in the players' mouths, they get very annoyed and leave the game.

Despite this, authors do have some control over their players' actions. The obvious solution is to not allow specific types of actions and claim they haven't been coded yet... if an author doesn't want players to fish, he doesn't code in the fishing sub-game.

More subtle control can be imposed by geography: In a dungeon, the player doesn't get to fight the dragon in its den until they figure out how to unlock the den's door. They can't unlock the door until they they get the key from the sphinx by answering riddles.

Other techniques are also possible:

In this sense, story writing is like composing music. In both forms of creation, the author/composer have complete control over every aspect of the form.

Designing a virtual world is like writing Jazz: Jazz composers write out chord progressions and occasionally recommended rifts; they do not control what the musician will play though, only guiding. Likewise, VW authors can recommend and encourage on (musical-like) themes, but not force strict adherence.

Many people reading this document won't be familiar with music theory, so I'll use a different analogy: Story writing is like arithmetic, while VW writing is like algebra.

Arithmetic deals with manipulating specific quantities, where the number, 3, is always 3, and 1+ 3 = 4. Algebra and higher mathematics deals with variables, such as X + 3Y = Z. The specific values for X and Y aren't known ahead of time in algebra. The mathematician must solve the problem for the general sense, not for a specific numerical solution as in arithmetic. The same goes for virtual world design; it's about controlling the overall flow, not the specifics.

Timing in virtual worlds

So what does any of this have to do with timing?

As I stated earlier, contemporary virtual worlds provide a set of sub-games (what and how) that the player can participate in. For the most part, the sub-games are laid out buffet style: If a player wants combat he visits the nearest hunting grounds or dungeon. If a players wants to fish, he walks a short distance to the nearest river. For chat, the player merely uses the built-in location-independent chat tool and talks to anyone in the world instantaneously. Trade is ostensibly localised, except for the omnipresent teleportation devices that nullify any sense of locality. Etc.

This is like writing a story where the player gets to pick and choose what elements of the story (backstory, characterisation, setting, action, etc.) will be next. While the freedom is great, some players will over-consume one component or the other, and walk away with a bad taste in their mouth.

Quests, on the other hand, are about timing and controlled use of sub-games. Despite the appalling quality of contemporary quests, many players head straight for quests because they begin to deliver what some players want... an expert hand controlling how backstory, characterisation, setting, and action are doled out, instead of random plucking at the piano. Unfortunately, contemporary quests fall far short of what they should be. There are a few reasons for this:

1.         Current virtual words do not include enough sub-games. Therefore, quests are limited to finding items/NPCs, combat, or delivering items/NPCs. If, for example, the VW had a chatterbot NPC sub-game, another type of quest could be added to the inventory; that of getting a NPC to talk. A chess sub-game could require that the player beats the opponent at chess. Etc.

2.         Contemporary quests don't string enough sub-games together, although this may because there aren't many sub-games. A piece of music requires more than a few notes.

Here is an example of what can be done in a quest when the VW has enough subgames to play with:

1.         If players wish to travel to a distant port city, they gather at the port and wait for a ship to leave. The ship won't leave until there are at least 6-10 players, or maybe a player with lots of money. This waiting game makes use of the chat sub-game since players will talk to one another when gathered. Furthermore, they will talk to strangers.

2.         Once on the ship, which is a boring place, the players could be asked to help out on the rigging or mopping the floor. Turn this into a sub-game, just as fishing has been in contemporary MMORPGs. Players wouldn't want to spend hours in the rigging sub-game, but a few minutes would be entertaining.

3.         In the distance the players see a pirate ship. Enter into the ship-to-ship combat sub-game, with the players controlling steering and cannons. If the players win, they make it to their port. If the lose, they continue to the next step. Any PCs that die in the bombardment get resurrected at the exit port.

4.         In a cut-scene (story sub-game), the players manage to grab hold of parts of the sinking ship and make it to a deserted island. The captain makes it there too.

5.         Once on the island, the players will realise they need food, or other supplies, plus they'll want to explore their surroundings. This allows for several sub-games, foraging, fishing, hunting, cooking, exploring. A hermit lives on the island; the quest can let him be found right away, or maybe not until the next day.

6.         At night, around the campfire, the captain could offer to tell a story of some sort, providing backstory.

7.         The next day the players will continue exploring. They find the hermit and talk with him; being on the island for many years, a chatterbot's frustrating conversations are a perfect match.

8.         Eventually the players learn about a secret cave from the hermit (which can't be found unaided). In the cave is the hermit's boat they can use to get off the island. They find the cave, using the exploration sub-game.

9.         Inside the cave is a monster that has taken up residence, using the combat sub-game.

10.      Eventually the players get the boat and escape the island. This shows another short story segment.

While the quest I've described steers the characters, it's much more fulfilling then the players choosing each item individually from a buffet, or from the shorter quests offered in most virtual worlds. Notice that many of the sub-games and activities, such as stories and ship-to-ship combat, used in the hermit-island quest do not exist in contemporary virtual worlds. Removing those elements from the quest produces a weaker experience. Re-arranging the sub-games also changes the experience.

Deconstructing the CRPG

Just in case you're not totally convinced about my concept of stopping the buffet-style sub-games, here's something to think about:

The traditional combat sub-game in a VW can be further deconstructed into sub-games, the most basic of which is a fight-game like Mortal Combat (although CRPGs have much simpler combat). In a fight game, players can select their character and their opponent from a buffet. CRPGs and MMORPGs don't allow players to pick and choose their opponents. Instead, they control the opponents, using PC levels and locations as a method for control.

Which is more fun, a CRPG where you can pick and choose your enemies and fight in your chosen arena, or a CRPG where you wander through the world and encounter enemies of the world's choosing?

Likewise, an adventure game such as Myst is a world containing many puzzle sub-games. Which is more fun, the puzzles as a buffet, or tied into a whole?

Conclusion

Just to reiterate:

1.         There are more sub-games than just killing monsters. (See Virtual World as Platform.)

2.         Use sub-games that encourage synergies between the sub-games. (See Virtual World as Platform)

3.         Think of sub-games as a tool-set to be used selectively to create a larger experience, not as a buffet for characters to choose from. If you wish a buffet, consider using quests as the items on offer, not the sub-games.

4.         Of course, you can always have sub-games available at the buffet. You might consider having the sub-games be location dependent, or to vary somewhat with locations, so players would have to travel (and experience other sub-games) to get to the sub-games they're looking for.

5.         How you combine the sub-games into an experience is very important.

6.         Quests cannot be produced from one or two sub-games alone, just as music requires more than two notes. Too few sub-games results in standard VW quests, aka: fedex quests.

 

 


 

Junk Food Entertainment

(Back to TOC)

17 November 2004

by Mike Rozak

What is the difference between an airport novel and a classic novel?

Partly, it's the reason why you're reading it; an airport novel is designed to kill time while you're on an airplane or in an airport, a classic novel is assigned to be read by an English professor.

Of course, a classic novel will be better written, have more interesting characters, and better plots. It will have been written sometime in the distant past, which is why it is called "classic", since it has withstood the test of time.

However, plenty of airport novels are just as well written (if not better) than some of the classics. What then allows an airport novel to avoid obscurity? Some classic novels were merely lucky enough to be singled out by English teachers. They may also have been written by an author that produced other classics, and by virtue of their siblings, be raised up.

Classic novels also have a quality that transcends an airport novel: Classics are "food for the soul", to use a cliche term. Amidst all the interesting characters and plotting, are ideas about life, the universe, and everything.

For example: Charles Dickens' "Oliver Twist" was not only a quaint story about an orphan, it was a larger exposition on 19th century England's approach to orphans, child labour, crime, and the lower classes. An airport novel is about, well, nothing.

For example: The movie, "2001: A Space Odyssey" is remembered while other science fiction moves from the late 1960's are long forgotten. 2001 was about humanity's evolution from ape, to human, to beyond, and ultimately questions if man will become god and create its own life-form.

Continuing on with the "food for the soul" analogy... As I see it, there are four different levels of "nutrition" in entertainment:

What does this have to do with virtual worlds?

Most virtual worlds fall into the "rice cake" or "junk food" categories. They are either used to kill time (orc-massacre marathons) or are entertainment without substance. As such, the existing crop is doomed to historic obscurity, other than their being remembered as pioneers.

Making nutritious virtual worlds

What can be done to make a virtual world more than empty calories?

I'm not entirely sure, but I'll give it a go. Let me first revisit what makes a classic novel or movie feel "nutritious".

How does this translate into a virtual world? A nutritious virtual world is one which either changes the player, and/or changes the world. Of course, this definition has nothing to do with "food for the soul", but it's a bit more concrete.

Richard Bartle, in his book, Designing Virtual Worlds, spends a lot of time discussing how players change over the course of playing in a virtual world. They undertake the hero's quest and find their true selves over the course of role playing and "trying on" other personalities.

My thoughts are not as grand as the hero's quest, but the hero's quest could be included as one of a virtual world's nutritious components.

Here are some ways a virtual world can change the player's real-world life:

And change the real world:

While a virtual world can change players and allow players to affect other players, it also acts as a surrogate for the real world. For example, if a person wishes to be wealthy in the real world, but cannot, he may make it a goal to become wealthy in a virtual world instead. Some examples of the virtual world acting as surrogate are:

Conclusions

I have digressed a bit from my original discussion. Originally I was talking about applying the concept "food for the soul" to a virtual world, and ended up with a list of reasons why people like playing virtual worlds...

I suppose I made a switcheroo.

What I have actually come up with is a list of "non-fun" reasons why people play virtual worlds. After all, learning a new language or owning a pet aren't fun by themselves. However, the act of knowing a language and then travelling to a different country is fun. And the 10 minutes a day you play with your pet is fun, even though the 30 minutes spent feeding it and cleaning up after it aren't.

My suspicion is that people begin playing a virtual world because it's fun (see Evolutionary Explanation for Entertainment), but they continue to play a virtual world because of other the "non-fun" reasons. (Although the virtual world must continue to still be fun.)

If this theory holds true, then there are some ramifications:

 

 


 

Steady-state approximation

(Back to TOC)

17 November 2004

by Mike Rozak

A few days ago I tried to imagine what virtual worlds would be like in 10 to 20 years. Of course, they'll have better graphics, better network connections, better AI, and better everything.

However, my mental wanderings chanced upon a few aspects of virtual worlds that may change:

1.         The lifecycle of future virtual worlds will shorten.

2.         The amount of time a typical player spends in a virtual world will shorten.

Let me explain...

Lifecycle of a virtual world

Historically, the lifecycle of a virtual world has been very long, even among MMORPGs. Some MUDs have been running for 15+ years. Meridian 59 (though resurrected), Ultima Online, and Everquest are still running after 6+ years.

I don't think this longevity will continue in the future. In fact, I suspect it has already stopped, even though people haven't realised the fact. UO, M59, and EQ won't necessarily die right away, but they will fade away.

To explain why, I want to describe three different categories of players and what cycle of a virtual world they are attracted to:

Part of the reason there are three types of customers for a virtual world is that virtual worlds can only support a limited number of players at any one time. Publishers need to limit a VW's population because, while adding new servers is relatively easy, getting new product-support staff up to speed is much more difficult. As a result, publishers will encourage a consistent playerbase population rather than a burst of activity followed by a quick VW death.

For the the last seven years, a major virtual world has come out about once a year. Meridian 59 was first, the Ultima Online, Everquest, Asheron's Call, Dark Age of Camelot, Anarchy Online, etc., all about a year apart.

In the last 24 months, major virtual worlds have been coming out once every six months. These include Asheron's Call 2, Star Wars Galaxies, Lineage II, Finaly Fantasy, etc.

Over the next 12 months, a major virtual world will be out once every 3 months. These releases include Everquest II, World of Warcraft, Dungeons & Dragons online, Middle-Earth online, Dark & Light, etc.

If this trend continues, even at the rate of a major world every 6 months (let alone every 3 months), the playing field will be swamped with virtual worlds. Not only will the new ones be contending for players, so too will the old ones, keeping up-to-date with semi-annual add-on releases.

So many virtual worlds provides players with lots of choices. What will each category of player do?

"Yes," you might be saying, "but a virtual world can keep putting out expansion packs and stay up-to-date with the latest technology so that it never dies?"

Expansion packs increase a product's longevity, but they only go so far:

"Yes, but MUDs have been running for 15 years, sometimes with the same players sticking around, mostly for social reasons." I agree that happened in the past, but I expect future behaviour to change:

Scale of virtual worlds

Speaking of mass-market players: As virtual worlds become more popular, the number of hours that a player stays in a virtual world (before they get bored and leave) will shrink.

Contemporary virtual worlds are designed to keep a player around for 500 to 1000 hours. (40 hours/month x 12-24 months) For brevity, I'll just call this "1000" hours of gameplay.

Now for the obvious question that I've never heard asked: What percentage of the potential game-playing population would be willing to commit 1000 hours to a game? 100 hours to a game? 10 hours? How do these players differ?

Personally, I last between 25 and 75 hours on a game. When I play a 1000-hour virtual world, I don't expect to stay any longer than 25-75 hours, not just because I get bored, but because I'd rather experience the variety of ten different games than one really long one. I suspect most game players are the same. Most people, those whose computer game-playing experience is limited to solitaire, might even find a 10-hour game to be a stretch.

Profile of users

1000 hours

  • People with lots of time on their hands (teenagers, unemployed, under-employed, retirees) and die-hard fans.
  • 5-10% of the potential game-playing population.

100 hours

  • Singles and occasional couples. Childless.
  • 15-30% of the potential game-playing population.

10 hours

  • Couples. People with children. People not interested in a really long game. First-time players.
  • 60-80% of the potential game-playing population.

You might argue that someone with only 100 hours of time could play a 1000 hour game; they just wouldn't get to the end. While this is possible, I don't think it will be common. There are a few reasons:

I haven't tried to convince you that a 100-hour virtual world can exist, let alone a 10-hour experience; I try to do so in this document. However, if one can exist, then the experience will be radically different from the 1000-hour game. Here's why:

What players do...

1000 hours

1000-hour virtual worlds are very similar to contemporary MMORPGs:

  • 1000 hours is a long time. It's enough that players can meet and befriend other players, as well as become enemies. Players may play with their real-life friends, but they are bound to meet new ones in the world.
  • Players are encouraged to make friends and enemies. Much (if not all) of the action involves other players. Game AI is a distant second.
  • Players are encouraged to join guilds.
  • Because the experience is all about interacting with players, shards will be large, on the order of 10,000+ players each.

100 hours

100-hour virtual worlds are similar to CRPGs and adventure games, except that they're online:

  • 100 hours is enough time for players to meet and adventure with other players, but not enough for close relationships or to make enemies. Players are likely to play with their real-life friends.
  • Players may group, but they're more likely to interact with game AI than fight/compete amongst themselves.
  • Players are encouraged to join parties. They can also play alone.
  • Since the expectation is for players to join a party and then explore game content, shards will be small, about 100-1000 players per shard. Of course, a designer could create a 100,000-player shard with private dungeons, but then what's the difference? (Except for crowded cities.)

10 hours

10-hour virtual worlds aren't similar to any marketed game, because at the moment, 10-hour games cannot survive financially. My best guesstimate is that they'd be like short adventure games, perhaps interactive stories not that different in feel from Choose Your Own Adventure books, except with much more sophisticated branching.

  • 10 hours isn't enough time to socialise. These virtual worlds will most likely be played by a group of real-life friends, or by individuals.
  • Players may group, but they're really in the game to interact with game's content.
  • Players may play with known friends or alone. The VW might provide a "dating service" that allows strangers to meet up for a game.
  • Private regions will be used for each player or group of friends.

 

User interface...

1000 hours

  • Because the player will use the same character for 1000 hours (unless there's permanent death), the game must allow for total customization of the character. Similarly, with 10,000 players in each world, characters' visuals must be unique.
  • Since the player has plenty time to learn the user interface, on average, the UI is more complex than the 100-hour and 10-hour games. However, because these games are mass-market, there won't be as much variation in the UI complexity.

100 hours

  • Since the character will be used for 100 hours, the character can be customised, but not as much as the 1000-hour game. Characters may be limited to specific classes or other archetypes.
  • On average, the user interface is simpler than the 1000-hour game. However, because so many 100-hour VWs are produced, some 100-hour VWs will be targeted at niche markets, and have complex UI and functionality.

10 hours

  • Because the game is so short, players will only be able to chose a template from a menu, such as the ability to play "Batman", "Robin", "Bat-girl", or "Cat-woman".
  • The user interface for all 10-hour VWs will be almost identical, just as the UI for all movies, television shows, and books is almost identical. Each VW will have one or two "special features" like a magnifying glass for a detective VW, or a sword for a fantasy VW.

 

Business model...

1000 hours

  • Due to content-creation in bulk, content is 1/4(?) as expensive as a 10-hour virtual world, but there's 100x as much. Therefore, the world costs 25x more to create than a 10-hour virtual world.
  • The virtual world can charge a monthly fee or make money by selling virtual goods.
  • Since content data will be so enormous, a DVD retail package will be needed for distribution. (Contemporary MMORPGs are 2.5GB+, which is downloadable by only the very patient.)
  • Because of the enourmous download size, a 1000-hour virtual world doesn't really need an online component for piracy protection. However, a single-player 1000-hour game is a bit unlikely due to the costs and low number of players.
  • A player only goes through 1.5 games per year.

100 hours

  • Content is 1/2 as expensive to create as a 10-hour VW, but there's 10x as much. A 100-hour VW costs 5x as much as a 10-hour VW to create.
  • Because a die-hard player can get through the content in a month, while a less committed player may take six, the VW cannot charge a monthly fee. It must either charge up-front fee, or make money through advertising. (A large company could group several 100-hour VWs into a package and charge a monthly fee.)
  • The package can be sold on a DVD, but is most likely a download since retail shelf-space is difficult to get.
  • The VW is small enough to download, so it needs an online component for piracy protection. The game might still be played by a single player in a private online instance.
  • A player can play through 15 games per year.

10 hours

  • Requires much less money to create than either the 1000-hour or 100-hour VW.
  • The game company must earn $1-$3 per game (which is too small to charge on a credit card) or be supported by advertising. (A large company could sell a "subscription" that includes a new game every month. Smaller games could also be bundled with 100-hour and 1000-hour games.)
  • 10-hour games are either downloaded or included in DVD game magazines.
  • They require an online component for piracy protection. The game might still be played by a single player in their own private instance.
  • A player can go through up to 150 games per year.

Hours of content vs. number of players

Notice the correlation between the number of hours it takes to complete a virtual world and the number of players in a shard. If there are 100 players in a 100-hour virtual world, then statistically, each player will be 1 hour away from the other in terms of content. At this density, players will occasionally run into one another by accident, or if they go to a focal-point like a village. If 1000 players are in a 100-hour virtual world then players will only be 6 minutes apart, and will be running into each other all the time.

Graphing the hours-of-content vs. the number of players (online at a given time) produces an interesting table:

10 hours

100 hours

1000 hours

0 players

Book, TV mini-series

Book series, TV series

Long-running TV series

1 player

Short single-player game. I have only seen amateur text-adventures that are this short.

Single-player game, such as CRPG or adventure game.

Very long-running single-player game. This category is unlikely due to the expense.

10 players

10-hour VW that I described above... Interactive storytelling with friends, like "Host a Murder".

Multiplayer CRPG like Neverwinter Nights.

Players will never run into one-another unless they pre-arrange it. This category is unlikely.

100 players

Chat

100-hour VW that I described above

MUD-like

1000 players

1000-hour VW that I described above... MMORPG-like

10,000 players

Chat

I put chat into the lower-left corner because a virtual world with far more players than content just turns into a large chat room.

If you don't quite get what I mean by "0 players" then see Virtual World Spectrum.

Conclusion

Producing a 100-hour virtual world results in a significantly different experience that the contemporary 1000-hour virtual world, as well as a different user base. Likewise, a 10-hour VW even more different and caters to a different set of players.

Are 100-hour and 10-hour VWs financially feasible? I don't know.

 

 


 

Choice

(Back to TOC)

17 November 2004

by Mike Rozak

Chris Crawford's book, "Chris Crawford on Interactive Storytelling", points out a very useful rule about interactive fiction (or storytelling) design:

A choice is not a choice if:

1.         The choice is made blind, where the user has no way of knowing which is the best choice. Asking a player if they want to choose what's behind curtain A or curtain B is irrelevant if both curtains are identical.

2.         The effects of the choice result in the same outcome, such as two doors that both lead to the same room. (For example: Alice Springs' airport has 4 side-by-side, glass, departure-gate doors that all lead to exactly the same place, a covered pathway that guides passengers to the airstrip.)

3.         The effects of the choice all result in "end-of-game" except one, such as the Dragonslayer video game where one of two choices almost always resulted in death.

I agree with these observations.

They effectively put an end to a Choose Your Own Adventure (tm) style game where the player reads some narrative, choses from two to four possibilities, which then leads to more narrative.

The reasons are:

1.         Each choice is a branch in a tree of possible paths the reader can visit. Even if there are only 8 branches, with 2 choices each, that's 2 ^ 8 = 256 possible endings, with 511 narratives that need to be written. Obviously, this is too much work, writing 511 narratives when only 8 of them will ever be experienced by any one player.

2.         To get around this, many CYOA-style interactions have branches that quickly reconnect with the main branch, breaking rule #2.

3.         To save work, they also have choices that result in instant death (or the story otherwise ending). This breaks rule #3.

Chris Crafword, as well as other authors that have made the same point, then goes on to dismiss this form of interactive fiction as a dead end. (He points out one "way out" that I'll get to in a bit.)

Is it really a dead end?

The escape clause

Chris Crawford's escape clause is that two paths can reconnect if they somehow affect the narrative later on. Even if they both lead to the same room, if one door makes the character good looking, while the other makes the character strong, and these attributes are used later on in the experience, then the choice is valid.

In the Stop the buffet article I wrote, I described a slightly different take on how to write quests for virtual worlds. Contemporary quests inevitably start out with a bit of short narrative explaining why the quest exists, followed by the player going to either kill a monster or deliver an item, and are completed with the character's return and follow-up narrative. Thus, the form for a quest is:

NKN or NDN

N = narrative, K = kill, D = delivery

Contemporary virtual worlds are limited to such simple quests because they don't have many sub-games in their palette. Most have a few other sub-games, like gathering raw materials (G), exploration (X), and chatting with other PCs (c). If they added more sub-games, such as conversations with chatterbots (C), and piloting ships (S), they could produce a more complex quest. The quest I described in "Stop the buffet" was:

NcNSKNGNCXKN

Notice that there are no choices in the high-level narrative. Each sub-game, however, has choices; they don't affect the high-level narrative, but they do affect the players and their characters.

What if I added choices in the high-level narrative?

If I do, I run into the same fundamental problems that dog the CYOA books. One choice leads to another, and to another, and eventually there are just too many choices for an author to deal with.

Therefore, I'll use the same tricks that the authors of CYOA do:

1.         I'll reconnect branches. For example, I could provide a choice of talking with the mad-hermit chatterbot, or just following (F) the hermit around the island. Both lead to the same cave, where there's a monster to kill, and then the same final narrative.

2.         I'll provide dead ends. Actually, I already have put in dead ends; if a character dies at any of the points in the linear quest, the quest ends (for that character).

Both of these changes seem to break the rules, except that the sub-games, which are components of the larger narrative, rescue the situation:

1.         Talking to the chatterbot might result in the player characters improving their "Patience" skill, while spending more time searching the island would result in an improved "Stealth" skill.

Furthermore, even if no "physical" change were made, one player might find talking with a chatterbot more fun that tracking the hermit, while another would find the opposite. Providing this choice, even though it leads to the same outcome, produces a more enjoyable experience for the player. Of course, if the player can't deduce ahead of time which choice he would enjoy most, then this argument doesn't hold.

A more subtle effect of the choice is to allow player characters that have high "Charisma" a better chance with the chatterbot, and high "Stealth" a better chance of tracking. Thus, whichever way the character has specialised, he will be able to complete the quest.

2.         The dead ends aren't necessarily dead ends, only if the players fail at the sub-games.

The new quest "formula" might look something like this:

NcNSKNGN (CX|F) KN

While I haven't tried the concept out in reality (other than as a dungeon master years ago), the thought experiment works.

Applying this revised rules to CYOA and MMORPGs

Just to be sure of my logic, I thought I'd apply my revised rules to existing interactive entertainments...

The reason that CYOA books can't "break" the rules like I have in my hypothetical quest is that CYOA only has one sub-game, narratives (N). Any story with branches is just a series of N's.

NNN (NN|N) NN

It's not too interesting, is it? Well, it can be, but the narrative becomes absolutely critical to the experience.

Looking at slightly more advanced technology, you'll see that a typical MMORPG's buffet of activities is:

(K|D|G|X|c)

Of course, MMORPGs have simple quests that I've already mapped out. They also have dungeon crawls where the branches in corridors provide the menu of choices for the player. Each room is one of the sub-games, such as a monster (K), trap (T), or puzzle (P). A segment of a dungeon could look like this:

(K(T|P) | TK | KKK) K

The formula represents an intersection with four exits. One exit is where the PC came from. The other three choices lead to: A monster to be killed followed by a T-intersection with a trap down one way and a puzzle down the other (K(T|P)). The second choice is a trap followed by a monster (TK). And the third, is a series of rooms with monsters in each (KKK). All three directions eventually reconnect to allow the final fight with the "boss" monster. (K)

According to the revised rules, a dungeon crawl makes for an interesting series of rule-compliant choices. However, for the dungeon crawl to follow the rules, each hallway must somehow hint at what is down it. Shouts of evil laughter might be heard wafting down two hallways, and maybe a short jaunt down the silent hallway would reveal a skeleton of a previous adventurer that was caught in the trap. Without these hints, the player must chose a hallway at random, invalidating the choice.

Asheron's Call 2 had a few dungeons that amazingly managed to break the rule #2, about a choice resulting in different outcomes. In many of the AC2 dungeons I visited there would be a T-intersection. Going left would lead to a monster, and then lead back to the main trunk of the dungeon. Going right would lead to an identical monster, and then back to the trunk. The T-intersection was a useless choice because both choices produced exactly the same outcome.

Of course, in most MMORPGs and CRPGS, there isn't any hint at what's to come. The player cannot sneak up the hallway to listen for monsters because the monster AI is aware of the PC just as soon as the PC is aware of the monster AI. (Perhaps because there's often no way a player can indicate they're in "stealth" mode.) As a result, players are forced to walk blindly into every situation. Additionally, MMORPGs and CRPGs reward players for killing all of the monsters in the dungeon by handing out XP. Players are not rewarded for making intelligent choices and avoiding unnecessary dangers.

The same analysis can be applied to adventure games. The only difference is that the palette of sub-games in an adventure game is different than those sub-games available in a CRPG or MMORPG.

Conclusion

In some ways, none of what I've been pointing out is new. Anyone who has designed a dungeon for Dungeons & Dragons knows these techniques already. It's obvious that CRPG and (most) MMORPG designers also know them, at least intuitively.

I intuitively knew them from my experience being a dungeon master in high school. I never sat down and figured out why the rules worked though. Now that I have written them down, the concept has become much clearer to me.

 

 


 

The anti-MMORPG

(Back to TOC)

17 November 2004

by Mike Rozak

When I began my effort to design a virtual world, I wrote "The trouble with explorers". While attempting to determine why adventure games turned into MUDs, I came up with the following thought experiment:

1.         Write a text adventure game.

2.         Put it online and allow multiple people to be logged into the game at once.

3.         What happens to the design?

The problem I quickly came up with was that the adventure game that took me one year to write, was completed in 40-50 hours by most players. Because virtual world players spend an average of 40 hours per month in the game, most players would finish all my content within the month, some sooner.

At that point, they'd start whinging that there wasn't enough content and I'd need a way to create content quickly to stop the whines. The solution was to add automatically generated content, which usually amounts to combat with randomly generated monsters.

At that point, a MUD is born, and the adventure game dies.

The cost of content

The problem that online adventure games face is that content is too expensive to produce. A text adventure takes approximately 1 man year to produce 40-50 hours of content. A graphical adventure like Myst IV takes approximately 1-2 man years to produce 1 hour of content.

CRPGs are somewhat cheaper to produce per hour of content (since they are more amenable to automatically generated content), and they have a larger audience, so online games quickly turn into online versions of CRPGs. (This is not always the case, though.)

But, even an online CRPG is too expensive to create content for.

Here's why: The average virtual-world player is online for 40 hours a month. That's equivalent to completing an offline CRPG every 1 to 2 months. However, the virtual-world player is only paying $15/month, while a new CRPG every month costs the player $50/month.

Of course, this isn't an apples-to-apples comparison:

1.         Given the $50 retail price, the game developer only gets $15-$20. So, realistically, if the distribution and Internet were free, an online CRPG that charged $20/month could provide the full content.

2.         The Internet bandwidth to support a player is a few dollars a month. Add $2/month for the Internet.

3.         Product support is also expensive, and even though I haven't heard a compassion, I suspect support costs for an online game are higher than an offline game. Add a $3/month for extra support issues.

4.         If the player were to purchase an offline game every month, the game would come with a DVD chocked full of new graphics and sounds. MMORPGs do not (yet) send out a monthly DVD update to their players, so a MMORPG's audio-visual elements won't change every month. MMORPGs solve this problem by putting out add-on packages every 6-9 months. If a DVD were mailed to every player, every month, you'd add $5/month. I won't include this in my guesstimates since MMORPGs do not currently mail out monthly updates.

5.         Virtual worlds have a smaller player-base than offline games. 500K players makes a virtual world is a big success, while 1M-2M is a successful offline CRPG. Fewer players mean that each player must pay more for the same quality. How much more? Twice as much?

Basically, if the content were kept up to CRPG standards, players would have to pay $25 - $50 per month for their online CRPG. Since they're paying $15/month, the content either has to be of lower quality, or it will run out.

There's another problem with providing that much content. If a CRPG is 50 man-years of work, then producing 12 CRPGs a year is 600 man years of work. 600 people all developing one world is a logistics nightmare. Even if each team is given a different region of the world to own (which will happen), items from one region can and will be carried by PCs from one region into another, causing all sorts of game balance issues. And what about conflicts in shared backstory that are created as one team invents some backstory for its corner of the world that invalidates the backstory from another part? Even if you could get players to pay the $25-$50/month, a full-content online CRPG would still be a no-go.

Consequently, the virtual world developer cannot provide enough content. Most users will reach a point where they have consumed all of the content, and the content-flow eventually dries up.

The drought

What happens when players run out of content?

Players do one (or more) of the following:

The last four bullet items correspond to Richard Bartle's player models in "Hearts, Clubs, Diamonds, Spades: Players Who Suit MUDs", (http://www.mud.co.uk/richard/hcds.htm).

No developer likes their players leaving or whinging, so they do what they can to make the other activities more fun, perhaps as a stopgap measure. After all, the players decided that's what they wanted to do, so provide them tools that allow them to do what they want. The solutions are:

The developer soon finds himself spending most (or all) of his time producing and improving sub-games to keep the players occupied, and little (or none) on content.

The MMORPG is born from the ashes of the online CRPG.

The "fun" in a MMORPG mostly comes from the sub-games that allow players to interact with one another, with only a small amount of fun coming from the content, in the form of quests. Some MMORPGs, such as Everquest II, are more heavy on content, while some are almost entirely content-free.

The instant replay

Did you see what just happened? An online CRPG (which may have originally been a online adventure game) morphed into a MMORPG. It's like a state change where ice suddenly becomes water. Or rather, a structured entertainment suddenly turns into an unstructured-entertainment, a sand-box.

In the TV-world such a state change is equivalent to a TV-series, which can only be produced at a finite rate no matter how much money is thrown at it, trying to fill in the 6 days and 23 hours between the next episode with stuff that is vaguely related to the original content.

Such filler doesn't work for TV; viewers have to wait a week before seeing the next episode. Why should it work for games?

It apparently does work... kind of. Millions of people play MMORPGs, and while they whinge about the grind (aka: fillter in-between the content), they keep on playing.

What if MMORPG players are an atypical subset of the general population? What if the majority of potential players are turned off completely by the grind, and want their content back?

Personally, I think that most people would take content over the grind. I know I would.

The other choice

When players ran out of content, the developer consciously decided to reduce work on new content and create PvP code, economic games, fishing, automatic content generation, improved chat, and other grindy things. What would happen if the developers didn't pander to their players' demands, but instead said, "Don't bother us, we're working on content. Come back in a week or two."?

I can tell you exactly what would happen. The players would leave and never come back. There are a few reasons for this:

Therefore, the first thing that would have to change would be the payment method. Payment would either be one-off, or based on advertising.

The virtual world would also need a less-rude means of telling the user to leave, something along the lines of, "You have just completed all the quests. We're working on a new one titled XYZ, which will be available next Friday. You can continue to play the game of course, either replaying old quests or hanging out with friends." When the quest is available, players waiting on it could be automatically E-mailed, although the E-mails should be staggered so not all the players show up at once.

If the company is large enough, it may have several virtual worlds and could recommend the user try one of the other virtual worlds while waiting for new content. The company could even sell subscriptions to several of its virtual worlds in a package, much a set television shows are all "packaged" into each TV channel.

Would this work? I don't know. Television does it all the time, but I haven't found a virtual world that attempts it.

Ramifications

For the purposes of this document, I will call the developer's "other choice" an anti-MMORPG. A MMORPG is what results when players are encouraged to stick around once the content (quests) have been used up. An anti-MMORPG is what happens if the players are encouraged to leave (temporarily) when the content has been used up.

If this anti-MMORPG model did work, it would radically change the virtual world's experience. Here's how:

1.         When a player first subscribes to a virtual world they will play intensely until the content is used up, and then only show up as new content is created. (Or for social reasons.)

2.         As a result, players are likely to take part in several virtual worlds at once. Most players today only play one virtual world at a time; this has significant social ramifications.

3.         At some point, someone in the development team will create a histogram showing how much content a player consumes before getting bored and leaving the world. Even with an infinite amount of quality content, the histogram will show that 80% of players play for more than 50 hours, 50% of players play for more than 100 hours, and 20% of players play for more than 200 hours. (Or whatever.)

4.         In order to maximise profits, bean counters will realise that the optimum "size" for a virtual world is 150 hours (or whatever) of content, just like book publishers know how many pages a book should be, according to their market analysis. (... I say cynically.)

5.         As a result, the scope of virtual worlds will shrink. Shorter duration virtual worlds will, in turn, attract more of a mass-market audience. The mass-market audience will, in turn, want an even shorter virtual world. Exactly how short the final experience will be, I can't say, but it'll be much less than the typical 1000-hour experience that contemporary virtual worlds aim for. See Steady-state approximation for more thoughts.

6.         Shorter duration virtual worlds will be cheaper to make, resulting in more experimentation. There will be more variety, and some of the worlds may be experiences that would be unacceptable with a 1000-hour game. A 10-hour VW can go even further. For example, I'd be willing to spend 10-hours playing in a hopeless, totalitarian-controlled world like George Orwell's 1984, but not 100 hours, and definitely not 1000 hours.

7.         Since more people will play virtual worlds, and since the worlds take less time to complete, more virtual worlds will be produced. Either a few companies will produce most of the virtual worlds, and/or a suite of virtual-world development kits will appear. See below.

8.         If a virtual world is only 100 hours long, then somewhere between 100 and 1000 players will be in a shard to prevent overcrowding of content. (It would be possible to have a huge shard with private instancing, but what's the point.)

9.         If a virtual word is large, with 100,000 simultaneous players, and 100-1000 players per shards, there will be 100-1000 shards. With that many shards, developers will target individual shards at special interest groups (a left-handed golfer's shard), or at specific real-world geographies (Boston, NYC, LA, Madrid, and Cairo). The locality-specific shards are convenient because people (especially singles) can use them as a tool to meet people in the real world.

Both a MMORPG and an anti-MMORPG

Another possible configuration is to combine both a MMORPG and an anti-MMORPG. This configuration is commonly done, whereby a few hundred quests are provided for "newbies". When they graduate from the quests (aka: run out of content) they join the rest of the experienced players in PvP.

The combination seems to work, but there are problems:

As the number of virtual worlds increases, will players naturally segregate themselves into quest-oriented (anti-MMORPG) vs. PvP-oriented (MMORPG) worlds? Will a mid-point like Everquest II still exist?

How many MMORPGs can there be?

As a programmer, I like the MMORPG sand-box concept, since it means that I can put all my effort into writing code that allows players to interact with one another in fun and interesting ways. There is no content to develop either. (MMORPG developers will call a larger landscape, more monsters, and new spells content. I suppose they are, in a sense, but I see them more as elements upon which the quests, the real content, can be built.)

Looking at virtual worlds from a business POV, I don't like sand-boxes. The problems I have with building a sand-box are:

Virtual world as platform

A virtual world is internally subdivided into several modules, each one built (more-or-less) on top of a lower module. The internal structure of a virtual world looks like this:

Content - Quests, NPCs, special equipment, special monsters.

  • Server scripts (NPC personalities, plot lines, quests, etc.)
  • Client (3d models of specific NPCs, sounds specific to NPCs, etc.)

MMORPG-specific - PvP code and other modules designed to allow for player interaction.

  • Server code (PvP rules, economics rules, players building houses, etc.)
  • Client (Building UI, etc.)

World-dependent - Code and assets for a specific world, based on a genre. For example, this code includes the monsters and magical items from Middle Earth, along with the Middle Earth geography. This does not include quest-specific material and NPCs.

  • Server scripts (world map, item descriptions, races, etc.)
  • Client (3D models and animations, sounds, etc.)

Genre-dependent - Code and assets for a specific genre (such as fantasy), which can be used for any type of fantasy world (whether Middle Earth, Earthsea, etc.)

  • Server scripts (how magic works, weapon code, etc.)
  • Client code (3D models an animations, sounds, etc.)

World-independent - Any code or assets that can be used for any genre of virtual world.

  • Server code (networking, billing, laws of physics, NPC AI, etc.)
  • Client code (networking, 3D engine, etc.)

Actually, most virtual worlds are not divided this way because most developers don't have the mental bandwidth to see more than a couple years down the road. Most virtual worlds are developed as a one-off system, so all the modules as interwoven; this is a huge design mistake. A few companies, such as Turbine, have isolated the world-independent code from the rest. Ultimately, I think the best solution is to divide the modules as shown because it provides for maximum flexibility.

When an online-CRPG development team gives up on producing content and instead decides to produce a MMORPG, with PvP, economics games, players with building abilities, etc., the development team stops working on the content components of a virtual world (blue) and instead puts work into MMORPG-specific portions of the virtual world (yellow).

At the moment, virtual worlds are all MMORPGs, which means that not too many are built annually. As a result, almost every MMORPG has its own home-grown world-independent code, genre-dependent code, world-dependent code, content, and MMORPG-specific code. A few companies are trying to sell world-independent code, but I don't think they're being too successful. (I could be wrong.)

If, however, virtual worlds shrink in duration (as described in this whitepaper) and hundreds of them are produced every year, companies that sell the world-independent components will do well. After all, if a virtual world is short (100 hours), there's no need for home-grown technology at the world-independent level.

Furthermore, there's no reason that the same companies couldn't sell a genre-dependent component for fantasy vs. science fiction worlds. They might even sell a world-dependent component, containing a nondescript and generic fantasy/sci-fi world. Then, any company producing a virtual world could modify the generic world to their needs and spend more of their time on content.

This is why I think of a virtual world as a platform. Everything in grey (world-independent, genre-dependent, and world-dependent) can be licensed to companies whose strength is producing either the content, or the MMORPG-specific code. Alternatively, one company could write and keep the grey modules in-house, while providing dozens or hundreds of independent virtual worlds by changing the content or MMORPG packages.

Conclusion

MMORPGs are the way they are because designers de-emphasise content (quests) and emphasise automatic content (monsters), chat, and PvP interactions. They do so because content cannot be cost-effectively produced at a fast enough rate to keep their players occupied.

If a virtual-world developer were to tell users to take a break once they had used up all the content, the nature of the virtual world would change dramatically, including a reduction in size from a 1000-hour experience to a 100-hour experience.

Would a virtual world designed to provide a mere 100 hours of entertainment work financially? I don't know.

 


 

The end

(Back to TOC)

17 November 2004

by Mike Rozak

(Continued from The anti-MMORPG.)

Creating a virtual world (an anti-MMORPG) where players will only stay around for 100 hours introduces a new problem that contemporary 1000-hour virtual worlds (MMORPG) don't have: How does the player's virtual-world experience end?

In a MMORPG, the experience ends for the player when they get bored. Since MMORPGs want players to stay around as long possible, and since some players do, MMORPGs don't have official endings.

However, an anti-MMORPG would run out of content for some (perhaps most) players. Of course, when all the content is used up and no more will be added, the virtual world could tell the player "That's all folks."

This is unsatisfying for the player, however. Ideally, the "end" is the point at which the player finally completes something monumental in the world, like killing the evil overlord.

There are problems with such an ending in a virtual world; I'll get to this in a bit. But first, I'll discuss how stories deal with endings.

Story endings

Of course, everyone knows how stories end... "And they lived happily ever after." Well, not quite all stories.

A story ends when the protagonists major objective is fulfilled, or shortly thereafter. The major objective is a goal that the protagonist has throughout the entire story. The protagonist is usually given this goal near the beginning of the story, and it doesn't change unless there's a plot twist. (If you have never read any books on story-writing then I suggest you do so.)

Furthermore, the major objective is a unifying device. While the world in which the story takes place may be huge, the author only includes narratives that lead to the major objective being accomplished, or which somehow elucidate important aspects of the objective, characters, or world. In some ways, the end determines what happens in the story.

This brings up an important question for virtual worlds: How does the author determine what content gets into the virtual world? MMORPGs, because they have a very limited number of sub-games (see Stop the buffet) and because quests (the content) are only a small part of the experience, don't really worry about what gets in. As a general rule, any half-way decent quest-idea is implemented. However, when a world is smaller and there is more potential variety in quests/content, determining what gets in is more difficult. The ending may help filter out some content ideas that do not lead to the end.

The end of the story is met when the character "defeats the evil overlord", or whatever the plot may be. All the content of the stories is affected by this ending.

A virtual world could have an end-point of "defeat the evil overlord", but then problems arise in a multiplayer world. If player A defeats the evil overload, does the whole world shut down? Of course not, that would result in every world shutting down within a week of its inauguration. Instead, the evil overload somehow reappears just in time for player B to defeat him.

In the case of defeating the evil overload, infinite resurrection isn't such a bad problem because players leave soon after the overlord's demise and never notice the fact that he hasn't really died. (Of course, players know on an intellectual level that he can't die, but it's nice to have the illusion.)

The problem arises in the more mundane victories, such as when the player rescues NPC Sally's cat from the tree in which it's stuck. After player A rescues the cat, it suddenly manages to get itself stuck in the tree again so player B can rescue it, ad infinitum. This loop is fine so longer as player A never visits Sally again. However, if player A does see sally again, one of the following things happens:

None of these are really good solutions. The player knows they must happen, but they ruin immersion and make the virtual world feel more like Disneyland, where every one is put on a conveyer belt to watch pre-canned entertainment.

The problem with (most) virtual worlds is that they don't change. They can't change because change would ruin the experience for other players.

Change in virtual worlds

Inquiring how a virtual world can end has led to the problem of change, since often the act of getting to the end requires change. Most virtual worlds cannot change though. What's the solution?

Stories, of course, are based around change. No matter how good the writing and plot are, if there's no change in the story, it feels unsatisfying. In a story, change occurs in several places: The protagonist, major characters, the world, and the reader. For a discussion about change in the reader, see Junk food entertainment.

For example: In most stories, the protagonist "finds himself". Major characters will be affected and emotionally changed by the protagonists actions. The world will be saved from an evil overlord (in a fantasy setting at least). And, in a really good book, the reader will walk away slightly changed by the experience.

Two forms of stories do not involve change. The way that they manage not to change is interesting:

How does one apply this to a virtual world?

Change in the protagonist (and the player)

In a virtual world, the protagonist is almost always the player. Some players will role play and understand the difference between the two, but they are very rare. Game designers are aware of this and don't force players to role play. Occasionally, a game will force the protagonist to be a distinct personality, but the effect is jarring. (I have played a few adventure games where the protagonist has a self that changes like it would in a story; to me, it feels like control is being wrenched from my hands.)

Because the protagonist is the player, and the protagonist has no mind of his own, the virtual-world protagonist cannot be changed mentally, a key aspect of change in a story. Instead, change must be funnelled into physical differences, or the character's relationship in the world. CRPGs and MMORPGs go overboard with these two kinds of changes; as the player character advances from level 1 to level 100, they become infinitely more powerful and wealthy. Perhaps MMORPGs overcompensate for physical change because mental change is impossible?

Unfortunately, I'm at a loss for words... The word "mental" doesn't convey exactly the right idea. A character's ability to learn skills like "Swordsmanship" could be considered "mental" change, but that is not what I mean. I originally used "emotional" change, but it didn't convey the idea properly either.

It's not exactly true that the character can't be mentally changed; the player can be. If the players emotions and outlook on life are changed, then so to are the characters. This line of thought leads back to Junk food entertainment.

Change in major characters (other than the protagonist)

In a story, major characters also change, often as a result of the protagonist's actions.

How about in a virtual world?

In a multiplayer virtual-world, there are two types of "major characters". There are other players characters that are important to the player, and there are non-player characters (NPCs).

The other player characters can change just as the protagonist can change. Problem solved.

The NPCs, on the other hand, are a tricky matter, which gets back to Sally (whose cat is always stuck in the tree):

1.         If a NPC is mentally or physically changed by the player's actions, then the change would logically be apparent to every other player. This effectively means that Sally's cat can only be rescued once, by only one player, making content creation very expensive.

2.         NPCs are usually written to be static. More sophisticated change would require AI that includes memory, goals, and emotions. Contemporary MMORPGs do not support such AI.

3.         Even if NPCs were given AI, their intelligence and emotional complexity would pale compared to the illusion of intelligence and emotion conveyed in stories, let alone real life. Changing an AI that's obviously a bunch of algorithms isn't very satisfying.

MMORPG NPCs, however, do not change. In a MMORPG, the only characters that can change are other player characters, so the only meaningful interaction is with other players. (Of course, NPC monsters can be killed, but after one has slaughtered 10,000 orcs, all meaning has disappeared.)

Later, I'll discuss some tricks that can allow NPCs to change. They are only tricks though.

Changing the world

If a virtual world doesn't allow for important NPCs to change, then how is the world supposed to change from the start of the player's experience to the end? In a single-player game, changing the world over time is easy for the developer (relatively speaking).

MMORPGs, however, can't allow the world to change as the player works his way towards "the end" of his experience. The evil overlord can't burn down a village and kill all its inhabitants part way through to "the end". Either the village has always be burned down, or it never will be, since some players won't have gotten to the village-burns-down content while others have.

A work-around that I've seen used is to have villages closer to the evil overlord's castle be burnt down, still smouldering as though the destruction had just happened. The physical distance between the player character's starting point and the evil overlord's castle (the ending point) correlates to the player's progress towards the ultimate goal. The further east the player moves (towards the overlord's castle), the closer to the goal he is. To hide this simple rule, some games, like Dungeon Siege (single-player CRPG), make the path look more like an intestinal tract. The disguise is easy to see through. While the correlation between space and progress does often work, it isn't a great solution.

Solutions to change

Simply put, virtual worlds with multiple players and hand-created content cannot change. There are some tricks to work around this, and even allow the illusion of change.

First, I'll list some tricks that I've seen MMORPGs and single-player games use:

Some other possible solutions exist:

Back to "the end"

The reason I digressed from my discussion about the "end" of the game and talked about change, is that the two are interlocked. If the NPCs and world cannot change, then any ending will be unsatisfactory because when the player reaches the end he won't feel like he has accomplished anything. Even when being entertained, people like to feel as if they (or the character they're watching) have accomplished something.

To make an overly-simple heuristic: To figure out what quests/content should be in a virtual world, first determine what "the end" will involve. How should the player's character have change? How should the NPCs change? How should the world change? And how should the player himself change?

Once you know the ending, you can filter all your potential content/quest ideas through these requirements. Does getting Sally's cat out of the tree really lead to the overthrow of the evil overlord? If it doesn't, then get rid of it or change it so it does; maybe Sally rewards the player with an important item or tidbit. Maybe Sally's cat has a magical collar that the player can pilfer, or go back later an ask to borrow from Sally. Maybe Sally's cat shows up at the end and pushes the evil overlord over the cliff into a pit of boiling lava. (... I don't think so.)

While such filtration of content/quests is necessary, it is not sufficient. If the player doesn't see the world changing as they complete their quests then they will feel impotent. If the evil overlord doesn't wreak havoc on the world in front of the player's eyes then the evil overlord will feel impotent. To prevent this impotency, the NPCs and world must change. Since, in a multiplayer world, the NPCs and world cannot fundamentally change, some clever tricks must be used to hide the static nature of the world.

Back to "the beginning"

Stories spend the first part of the book introducing the protagonist and setting up the situation so that the protagonist wants to reach "the end". The beginning of the story must answer the question: Why does the protagonist want to overthrow the evil overlord?

A virtual world with an ending must likewise answer this question.

As we all know, the author of a story choses (or designs) a character whose motivations include a desire to reach "the end". Frodo Baggins wanted to defeat the evil overlord Sauron because he was a nice guy who wanted to protect the shire, and because he was forced into to... no one else wanted to carry the ring except Boromir.

In a 10-hour virtual world, the player's character will be given a specific identity and reason for being the protagonist, such as a choice between playing Batman, Robin, Bat-girl, or Cat-woman.

A 1000-hour virtual world (a MMORPG) lets the user choose their own identity and customise it to the Nth-degree. After all, the player will be stuck with that character for an awfully long time (unless there is permanent character death).

A 100-hour virtual world (an anti-MMORPG) will be in-between. The player may be given a choice of character templates who might wish to defeat the evil overlord, and reasons for their disliking him. The player will have enough choice in his character so that he will be able to choose abilities that he likes (some people like fighters, other thieves, etc.) while still having a character with motivation and ability to go after the evil overlord. Characters must also have enough variety that when two players meet they won't be twins.

Players don't need to limit themselves to one character though. Most MMORPGs already have a tradition of several characters per player, so the same is possible in an anti-MMORPG.

Multiple characters might even be desirable... they could be used in a "karmic" manner. Perhaps the first character the player creates is limited to a peasant. Once the character has completed a set of quests specific for the peasant "class", the player is informed that he can create a "freeman" character. The whole virtual-world experience could be a series of "reincarnations" from peasant, to freeman, to noble, to the overlord himself. Maybe the peasant will think the overlord is evil, while the overlord will think himself good. This would make for an interesting plot twist. Stories use the related approach of jumping into different characters' heads, but a virtual world could do a much better job by having the player live each of the characters' lives.

Conclusion

If a virtual world is only 100 hours long, it will have an ending. If it has an ending (and even if it doesn't), the NPCs and world must change to be satisfying. Traditionally, MMORPGs have neither endings nor change, while single-player computer games have both endings and change.

Ultimately, a 100-hour virtual world starts looking a lot like a single-player adventure of CRPG game except that players will encounter other players wandering around inside the world. They will be able to chat and team up with other players, but are unlikely to enter into PvP conflicts.

Is this viable? I don't know.

Maybe I'm barking up the wrong tree. Maybe the solution is to copy what's already being done: Those players that want a responsive world or 100-hour experience should play single-player games (adventure or CRPG), while those wishing a longer 1000-hour experience should play a MMORPG. Maybe any attempt at an in-between is futile.

There are some advantages to an "in-between" solution though:

 

 

 


 

Build it and they will come

(Back to TOC)

24 November 2004

by Mike Rozak

A few years ago there was a movie about a corn farmer who heard a voice in his head saying "Build it, and they will come." So, just like any normal person would do, he built a baseball field in his corn field, and teams of ghostly baseball players showed up to play.

"Build it, and they will come" is also an implicit design model of many virtual worlds, where authors build a virtual world and hope that (a) people will just wander into the new virtual world, and (b) some of the wandering people will like it enough to stay. Of course, neither of these statements are necessarily true; A virtual world needs advertising to attract players, and good design to keep them.

One question which virtual world designers don't seem to ask much (at least publicly), is "Who will come?"

Maybe they don't ask because the answer is obvious: 20% male teenagers and 50% 20-something males. That's the trend with MMORPGs, and to a lesser extent, single-player computer games. (The other 30% are divided amongst females and aged 29+ males. See http://www.nickyee.com/daedalus/archives/000194.php for the numbers.)

Why are teenagers and 20-something males so attracted to MMORPGs?

Or, to reverse the question: What features would be ideal to attract teenagers and 20-something males?

(NOTE: There are quite a few holes in this whitepaper's arguments. I almost didn't put it up, but I think it's worthwhile despite the holes.)

Attracting teenagers to virtual worlds

When I sit down and list what features would most attract a stereotypical teenager, here's what I come up with:

MMORPG features are a great match for male teenagers. Despite the monthly fee, around 20% of MMORPG users are male teenagers.

20-something males

The other demographic that seems to be abundant in MMORPGs in virtual worlds are 20-something males. I want to redefine the category slightly, into "competitive adults" and "non-competitive adults".

Competitive adults are mostly male (very few females); with a tendency towards being single males, aged 20-29. They are Richard Bartle's "killer" and "achiever" player types.

Non-competitive adults are both male and female, and fit into the "socialiser" player type.

Married players can also be grouped into competitive and non-competitive adults, but when they have children (which is a fairly common result of marriage), their free time plummets and they are less likely to play virtual worlds.

Competitive adults

The typical competitive adult in a MMORPG is a 20-something single male. If I wanted to create a virtual world for stereotypical competitive 20-something males I would do the following:

MMORPGs do pretty well covering the requirements of competitive adults. They do miss a few areas, most noticeably being the lack of real women (although virtual ones abound).

Non-competitive adults

Non-competitive adults are male or female, aged 20+. Most of the gameplaying kind are single, so I'll focus on them for the stereotype.

Non-competitive singles have very different requirements than competitive adults...

It's obvious why competitive players like virtual worlds: Competition with real people (as opposed to AIs) is more intense and meaningful. They cannot get the same satisfaction from playing a single-player game.

Why would a non-competitive person wish to endure the technical headaches and financial costs of playing a virtual world?

Socialisation, of course.

I suspect the main reason that many non-competitive singles would partake in virtual worlds, as opposed to playing a single-player computer game, is to meet other singles with similar interests, preferably of the opposite gender. Entertainment is also important, although I couldn't say which is more important, entertainment or socialisation.

You could argue that non-competitive singles don't behave this way at the moment, and I'd agree. However, if a world were targeted at them, and it made the obvious marketing choice of creating a shard per city, then non-competitive singles would use virtual worlds as meeting places.

If you agree with this hypothesis, then a virtual world designed for non-competitive singles would have:

Married adults without children are more likely to use virtual worlds as an entertainment than a virtual nightclub, although many will still be interested in meeting people. Married adults may have less time to play a virtual world, though.

Those with children are unlikely to have any time at all to play. If they do have time, they may wish to spend it online with their children. (See below.)

Other age-groups

Just in case you haven't noticed, I just segregated the user-base into different age-groups and figured out what a stereotypical member of that age-group would want in a virtual world. Stereotyping players based on age group divides the population into:

That only leaves the "children" stereotype to discuss...

Children (and their parents)

Children are not customers in their own right since their parents inevitably do the purchasing. As a result, both the children's and parents' interests need to be taken into account when designing a virtual world:

Other stereotype categories...

Of course, stereotyping your market based on age will only get you so far. It's better than nothing though, as long as you don't believe your conclusions too much.

Other divisions for stereotyping target markets exist, each with their own ways of attracting specific users:

Distance from reality

Most MMORPGs are fantasy or science-fiction based. This is interesting, because most television shows and movies are based on near-reality...

Let me explain what I mean by "near reality" and a few other definitions:

Far-reality includes:

The reason why having most virtual worlds based on fantasy or science fiction realities is surprising, is because most adults don't seem to like realities that are too distant from "reality". (Children don't seem to mind though.) Just take a quick survey of television shows and movies... Very few TV shows are based on reality, since that would be boring. Most are cop shows, hospital shows, and reality-TV shows, which are all near reality. A few occur in the near past, the near future, or include minor amounts of magic and alternate realities.

Most television shows and movies plant themselves firmly in a neighbouring reality. Very few venture further...

Basically, the further away an invented reality gets away from "reality", the less mass-market appeal it has, which is why virtual worlds based on fantasy and science fiction are surprising. (Also surprising is that reality-based virtual worlds, like the Sims Online, have done quite poorly.)

I suspect there are reasons why fantasy and science-fiction are the preferred virtual world though:

So what does this have to do with "Build it and they will come"?

I suspect that if you build a world based on near-reality, you'll get one type of player. A world based on distant realities, such as a dream world, will attract a completely different type of player.

You can boil this analysis down even further into a two-dimensional graph, with one axis being the amount of alternate reality in the world, and the other the amount of change to the laws of physics. The further from (0,0) a virtual world gets, the fewer adults will be interested.

Conclusion

Build it, and they will come... Yes, but you need to know who is "they" before you can build it. A baseball field built in a corn field will attract ghostly baseball players. A golf course in the Australian outback might attract entirely different sorts.

Most contemporary virtual worlds are built to attract:

1.         Teenagers and 20-something competitive male players.

2.         Technically minded players (as opposed to artistically minded).

3.         Players that like a weakly alternate world (fantasy or science fiction) with some weak changes to physics (magic and spaceflight).

Virtual worlds designed for these demographics have attracted the largest populations.

Worlds targeting other demographics have been less successful:

Demographics affect more than just the number of players that will be attracted to a virtual world...

The specific activities that the world provides are affected. For example, a car-racing sub-game would appeal to teenagers (because they don't yet spend an hour a day driving their car to and from work) and competitive adults (as a race). To attract people interested in alternate-reality you'd need to change the car to a spaceship, submarine, or battleship. To appeal to alternate physics crowd, the spaceship would need to warp space as a defence, or employ other non-Newtonian physics. To appeal to artsy types, players would need to be able to customise the car (or spaceship) with accessories and paint-jobs. (See Virtual World as Platform and The attraction of impossibility.)

The demographic also affects how long a player will be willing to spend. Teenagers will want longer games than most adults. (See Steady-state approximation.)

The sub-games and duration of a virtual world, in turn, affect everything about the world.

Maybe asking who "they" are is a very important question...

 

 


 

Intertwined storylines

(Back to TOC)

5 December 2004

by Mike Rozak

In The Anti-MMORPG, I concluded that a virtual world with 1000 hours of content wasn't viable, not just because of the expense, but also because only a small percentage of the original player base would be around by the time the 1000th hour of content was reached. After all, if only 80% of the users finish each 100-hour chunk (which is a high guesstimate), then only 10% (0.8 * 0.8 * ... * 0.8) will make it to the end, since each 100-hour chunk of content relies on the previous 100-hours.

However, I just thought of an out... one used (albeit poorly) in many virtual worlds:

A virtual world could be composed of independent but intertwining storylines. After all, many novels occur in New York City, but they certainly do not require readers to have read prior NYC-novels.

Some MMORPGs already use a similar technique: Evil characters begin in Evil-land, and good characters beginning in good-land. Evil characters and good characters each experience different quests, and consequently different storylines.

This division potentially allows the game developer to sell two packages: one called "Be a good guy" and the other called "Be a bad guy". Each one could be 100-hours of content, and a satisfying experience (with an ending) in itself. Players that really liked their first experience in the virtual world, could purchase the rival package and get another 100-hours of enjoyment. Those that had enough after the first pass wouldn't feel like they were missing out on anything, just as someone who has failed to read all of the novels based in NYC finds it easy to forget about NYC and read LA-based stories. Content development is cheaper for the virtual world company since it can use some of the content from good-land (like models of good races) in evil-land, and vice versa.

Current virtual worlds don't take full advantage of intertwined stories though:

So the next time you're designing a monolithic virtual world where any player can go anywhere and do anything, consider the alternatives. It's possible to have a virtual world with thousands of hours of content, divided up into shorter storylines targeted at specific personalities.

 

 


 

Player vs. X

(Back to TOC)

5 December 2004

by Mike Rozak

Virtual worlds are commonly categorised into those that a player vs. player (PvP), or player vs. world (PvW). In a PvP world, the fun comes from interacting with other players, using the world as a medium for the interaction. In a PvW world, the fun comes from interacting with the world that the author has created.

A third category of world exists, which is not so much "versus" as "creates" or "changes". Many players like to create objects in a world, and ultimately change the world. Worlds such as "A Tale in the Desert" and "Second Life" are often about creation.

Furthermore, several different elements of a virtual world can be used to entertain PvP, PvW, or creation players. These are:

I created a graph where one axis is a combination of PvP, PvW, and creation, while the other axis involves the elements of the world:

Player interacts with other players and their creations

Player interacts with the authors and their creations

Player becomes an author and creates elements of the world

People: Directly

Chat, bulletin board

Posts on bulletin board for feature requests

Players cannot create other players, although they can recruit them.

People: Characters

PvP combat, economics games, role playing

Some games have deities and major characters played by the live team

A service that allows players to create interesting characters for other players?

NPCs: Hand generated

Attacking a player's pet or henchmen

Conversations with NPCs, and quests

Allow players to acquire pets or henchmen and "program" the AI for them.

NPCs: Automatically generated

VWs currently don't allow players to create automatically generated NPCs, such as guards or armies, but they could.

Monster bashing

Allow players to hire and program automatically-replaced guards and monsters for their private castles and dungeons.

World: Hand generated

Players enjoy other players' housing or (in the case of Second Life) player created content.

Enjoying the world's puzzles, backstory, 3d models, etc.

Allow players to create housing, sculptures, artwork, and even object scripts.

World: Automatically generated

VWs currently don't allow players to create automatically generated world content.

Wandering around the wilderness (usually automatically generated).

Allow players to create the code that creates the world. (I doubt many players will be this skilled, and any that are will be hired by VW companies.)

Questions...

So what does this mean?

I'm not sure. However, the chart raises some questions, such as:

Can they co-exist?

Another question that arises is whether players who want different experiences can coexist? Can a world be both PvP and PvW? Can a PvP world allows players to create content? Or is PvW and a creation world possible?

In an attempt to answer this question, I listed out major design decisions that would be beneficial for a PvP, PvW, and creation-based world.

Player vs. player

To make a player vs. player world you need:

Player vs. world

To make a player vs. world game you need:

Creation-based world

A world that fosters creation would have:

PvP, PvW, and creation in one world... the compromise

Unfortunately, putting PvP, PvW, and creation in the same world produces conflicts, some of which are unsolvable. May virtual worlds try to cater to all three and produce a familiar compromise. (Items in red reflect serious conflicts between PvP, PvW, and creation.) :

The feedback problem

Creating a world solely targeted at PvP, PvW, or creation removes these conflicts, but does a world with fewer conflicts lead to an uninteresting world, as Richard Bartle has observed?

The PvP, PvW, and creation conflicts illustrate an even larger problem in virtual worlds:

A virtual world attracts certain personality types. Because people in a virtual world can see and interact with one another, the personality types interact and often positively or negatively impact the personality types attracted to the world. This sets up positive and negative feedback loops that are difficult to predict. Furthermore, the personality types of the existing player base affects what features the developers are asked to implement.

To use a traditional example: I like watching animated movies like Shrek. However, I don't like watching them in theatres because I have to put up with young children crying. Therefore, I watch Shrek at home. In a virtual world, I can't help but invite all the other viewers into my living room, so to speak. Would I watch Shrek if I couldn't detach the experience from the crying children? Would some people, such as other children, be attracted to Shrek more because of the other children than the actual movie?

According to Richard Bartle's experience, a world with only killers (a sub-set of PvP) will tend to die out, while a world with PvP and PvW players will be more stable. Similarly, worlds with only socializers tend to die.

So, while it's possible to design a world exclusively for PvP, PvW, and creation, such designs may not be optimal. Worlds that attract a combination (such as EQ1 and WoW) seem to have more players than specialised worlds (such as WWII online, Uru Live, and Second Life). (EQ2 seems to be solely PvW; it'll be interesting to see how many players it attracts.)

 

 


 

Choice, part 2

(Back to TOC)

5 December 2004

(Revised 13 December 2004)

by Mike Rozak

I had some more thoughts about my essay on Choice. The choices I discussed last time were choices that occurred within a quest. What about the choices that glue quests together? How are such high-level choices designed?

Weak choices

The standard MMORPG is topographically designed to be a large landscape with hills, mountains, rivers, and oceans. Within this landscape are gateways (aka: doors, dungeon entrances) to smaller, interior worlds. The interior worlds have walls and doors that restrict movement.

The "choice topography" for a MMORPG is similar to its terrain. At any point in the outdoor landscape, the player has the choice of moving NSEW. In the outdoors, players don't have many more "valid" choices beyond these four directions... Why?

Don't forget, valid choices are those that can significantly affect the outcome of the experience.

Other valid choices are occasionally available: For example, a player may choose to sit around and heal up. (Note: Healing rates in one region of the world are almost always identical to everywhere else, so healing is location-independent. Wouldn't the choice to sit and heal be more meaningful if certain parts of the world produced better healing? Maybe a character that sits in the shade by a pleasant stream would heal up quicker than on sitting out in the sun. A player would then have to chose where to heal, not just to heal.)

One would think that wandering monsters provided a choice about whether to attack them or not, but monsters (and other players), are location-dependent. They exist at a point in the 2-dimensional terrain, and only react to the player's character when he is a short distance away. Because monsters' AIs always seems to attack, you could claim that the choice of moving north towards a monster is the same as choosing to attack the monster. And, you could further argue that the choice of moving north to attack orc A, is essentially the same as moving east to attack orc B, except that at the end of the combat, the character has either moved north or east of his current location.

MMORPGs further limit choices by logically placing like monster species in the same region of the world. This means that the part of the virtual world populated exclusively by giant rats only allows players to move and avoid attacking a rat, or move and attack a rat. If there were giant hedgehogs, flying squirrels, and a handful of other nasties, the player would have more choices. (Assuming that attacking the other monsters actually took different skills and strategies.)

So, with a few exceptions, each point in the wilderness allows the player a choice of moving north, south, east, or west. Attacking a monster is implicit in a PC's movement, and healing really isn't that much of an issue.

A large world will be 100 km x 100km, or 100,000m x 100,000m. Realistically, every 10m x 10m section is a choice of location, so a wilderness map has 10,000 x 10,000 choice nodes where the player can chose to go NSEW. Even though a player only has four choices at each node, with 100 million nodes, they still have a lot of choices.

I claim there aren't really that many choices though. Let me postulate a rule:

A choice whose consequence is easily and reliably undoable is not a choice.

Since the character can quickly walk east, and then just as quickly return to the same spot by going west, moving east does not count as a choice. If a monster were east of the player, or even if a monster might be east of the player, the choice might matter. However, in the wilderness, a player can see the monsters long before they're forced into combat, so there is no chance that the monster "might be east of the player". A player almost always choses to engage or not engage a monster.

Of course, if the player walked 1 km to the east, that would be a significant choice because walking 1 km east and then undoing it by walking 1 km west is a lengthy process. If this is the case, which eastward-step was the one that was the choice? (Aka: Which straw broke the camel's back?)

Oops... I'll rewrite my rule:

A choice whose consequence is easily and reliably undoable is a weak choice.

Therefore, while the MMORPG wilderness provides players with billions of choices, they are all weak choices, and don't count for much.

Interior exploration is slightly better though, because a monster could be hiding around the corner, and doors might only let players pass through one way. Still, the choices are still fairly weak.

As a general rule, any place in a contemporary MMORPG that can be gotten into can easily and reliably gotten out of. Unless, of course, the user makes an exceptionally stupid decision and runs head-long into a dungeon that's labelled as "very dangerous". Chris Crawford argued that providing players obviously stupid choices don't count either, since no one will ever choose them. (Incredibly stupid choices also lead to instant death and an end to the story, which likewise invalidates the choice.) I tend to agree.

Just as an aside, MMORPGs also weaken other choices beyond just movement:

String of pearls

If you have read any books about adventure game or CRPG design, you'll have read about "the string of pearls" approach to design. They define a "pearl" as any region of the world that PCs can freely wander around.

My definition of a "pearl" is any region of space where the choices to move about are all weak. The "string" part occurs where the player makes a choice that cannot be easily undone.

An adventure game, such as Syberia, used the string of pearls approach. The first pearl of the game involved figuring out how to get a clockwork train to operate. When the player finally got on the working train she entered the string, which was a cut-scene that placed the player in a new pearl. This new pearl occurred at a university; there was no way for the player to go back, and (once again) to go forward the player needed to get the train moving again.

Pearls can have more than one exit; Syberia could have had an airplane that would also allow the player to leave the pearl. But if there are several exits from a pearl, do they end up in different locations?

If they wind up in different locations then content is wasted since the player cannot go back (except by replaying the game). If the strings all quickly end up in the same location then what's the point of the choice?

The laws of choices resurface... if there is more than one exit from a pearl then the multiple exits form a choice. Since it is a choice, it most be a valid choice. Strings leading to dead end pearls are not choices. Strings that quickly reconnect to the main path invalidate the choice. Etc. The strings must lead to significantly different experiences, even if they ultimately reconnect.

A choice which moves the player into a new pearl is a high-level choice because the choice results in the loss of significant amounts of content that the player will no longer be able to access. (Other types of high-level choices exist, which I'll discuss later.) The pearl is not always a physical location; sometimes it's as ephemeral as a choice of following the dark side or light side of Star Wars' "force".

Of MMORPGs, pearls, and other things

If you acknowledge that MMORPG geography is composed of weak choices, and that a pearl is a collection of weak choices, then contemporary MMORPG's are just a single pearl. They have no high-level choices in their topography.

Not quite...

MMORPGs do have high level choices. Most of their choices are mid to low-level however, like choosing to fight a monster of flee from it. Most high level choices in a MMORPG don't come from the content, but from the social relationships in the MMORPG. Saying or doing the wrong thing (or right thing) to another player could lead to irreversible changes in the player's experiences.

To use a fairy-tale example: Being rude to a needy low-level player could turn out to be a serious mistake when months later the low-level player is now the uber-player and looking for revenge. (Technically, this scenario presents an invalid choice because the player has no way of knowing that the beggar in front of him will become king. But that's life...)

Some other important choices that MMORPG players make are:

Again, MMORPGs come up fairly weak in the choice department. Social relationships are obviously strong choices. A player's choice of race, class, and realm are also important, but they're invalidated because a player is forced to make them when they first start. Some MMORPGs solve these problems by allowing players to choose their class and realm later on in the game. One could also argue that players commonly toss out characters, allowing them to make reasonable choices on their second attempt.

Are there any other high-level choices that MMORPGs could present to players?

None of the ideas I presented are really new. I've seen them used in various places, but it's nice to see them written down.

Conclusion

Choices occur at many levels: Sub-games (like combat) are full of choices about what combat move to use, etc. These choices are bracketed by the mid-level choices in quests that I described in my last write-up, Choice. Mid-level choices are affected by high-level choices that I just discussed. And of course, high-level choices are affected by what game a player choses to play.

For a choice to be strong, it must include consequences that cannot easily be undone. For a choice to be high level, it must be based on major consequences that are very difficult to undo.

Ultimately, choice is about consequences, and a virtual world is limited to one or more of the following consequences:

Low and mid-level choices frequently result in small losses of time and less enjoyable experience.

High-level choices, to be important, have to threaten larger losses. Unfortunately, for one reason or another, none of the possible consequences get used. Losing 10's to 100's of hours of play would be unacceptable to most players. Likewise, if an "unenjoyable" consequence causes players to do 10 hours of math problems, many will leave. Developers don't like having large chunks of content lost to players since it costs money. And no developer will try and cause players to lose friends.

As a result, most MMORPGs don't have any high-level choices other than those created by social relationships in the world. Poor design doesn't help, either, since some of the few major choices a MMORPG provides, such as the character's race or class, are made by new players who don't have any inkling of the consequences of their choices.

 

 


 

Small VW operators vs. large

(Back to TOC)

5 December 2004

(Revised 8/12/04)

by Mike Rozak

Seeing as I'm thinking about running a small virtual world, I have spent many hours figuring out areas where small virtual world providers (with 10K users) have an advantage over large virtual world providers (with 1M users), and vice versa.

Obvious tradeoffs

According to my definition, small VW providers will have about 10K users and large VW providers will have around 1M users. That means large VW providers have approximately 100x as many users as small VW providers; since small VW providers won't be able to charge much more than large VW providers, that also means large VW providers have 100x the resources to invest in the world.

Despite their small budgets, small VWs need to produce entertainment at a "quality" that's on-par with large VWs. Luckily, "quality" is subjective, and depends upon the user's perspective. Some people think great graphics are a very important part of quality, while others do not. Some players may rank customer support higher. Etc.

This 100-fold difference in resources forces small VWs to make the following design concessions:

Other cost-cutting techniques

Small VWs have to cut costs as much as possible. Here's how:

Ramifications of lower quality graphics, animations, and sound

Once a VW decides to use lower quality eye candy, there are some ramifications:

Ramifications of a niche market

A niche market allows a small virtual world to incorporate:

Ramifications of fewer players

Small VWs will have fewer players, which allows for:

Advantages of large virtual worlds

Amongst all the advantages of small VWs, I also listed several advantages for large VWs. Large VWs will have some other advantages:

Conclusion

Small VWs will provide different experiences than large VWs, just as books are different than movies. The key differences between small VWs and large ones are:

In other words, when considering what a large VW will be like, think of McDonalds and commercial television. Small VWs will be more like neighbourhood restaurants or novels.

 

 


 

Automatically generated content

(Back to TOC)

5 December 2004

by Mike Rozak

Being a programmer, I feel like I'm wasting my skills when I work on content, like drawing 3D models, designing conversations for specific NPC encounters, etc. I'd much rather be programming, since that's what I'm skilled at... and after all, anyone can create content.

Of course, this last statement is a lie. The truth is, that I know I'm very good at programming, and I know I'm not as good at content creation.

Furthermore, while not anyone can create content, virtually every virtual world will be based on hand-created content. There will be many more hand-crafted virtual worlds than there will be automatically-generated ones because writing all the generation algorithms and dealing with all the unexpected interactions is a very difficult task for a very small market. (I expect that most players will prefer hand-generated content.)

As a programmer, I'd still like to create a world that is 99% computer generated. It just sounds fun... The algorithms to automatically generate content are a fair amount of work to do, but once they're done, the content flows out of them without any effort. (Writing the code to automatically generate an X, such as a tree or dungeon, is usually 10x - 100x as much work as producing a single version of X. So, if a world has 100 or more versions of X, automatically generated X's are a time saver.)

Contemporary virtual worlds already use a bit of automatic content generation, most notably:

Of course, I think big, and can go even further:

By the time all these systems have been implemented, I would have a virtual world that is functionally equivalent to most hand-generated MMORPGs, for only a fraction of the cost.

Unforuntately, computer-generated content has a major problem:

Some people do enjoy playing in automatically generated worlds, such as Rogue, which lets players adventure through a randomly-generated dungeon. Automatically generated content has the following advantages:

Boiling down the list, it's clear that automatically generated worlds attract the following people:

I suppose that, in the end, it's all a tradeoff, and the decision depends upon what you're interested in, and what kinds of users you wish to attract.

 

 


 

The authoring equation

(Back to TOC)

9 December 2004

by Mike Rozak

A 3D computer graphics image is generated from hundreds of equations and algorithms that simulate how light reflects and refracts off of objects. There are so many equations and algorithms that it's easy to be overwhelmed.

However, computer graphics has a single unifying equation, called "The Rendering Equation". I could write it out, but most people would be afraid of the math, so I'll use words instead. All the rendering equation says is that the colour and brightness of a pixel is determined by the light reflected, refracted, or shined into that pixel by the objects in the scene. The light reflected or refracted by the object (to the pixel), is in turn determined by light reflected, refracted, or shined by other objects in the scene. Repeat as many times as necessary.

One simple equation makes sense of all the 3D graphics buzzwords, such as radiosity, ray tracing, global illumination, etc. The reason why all these buzzwords exist, however, is because the rendering equation is very slow (to draw). The buzzwords are just optimisation techniques whose complexity masks the simplicity underneath.

The authoring equation

In an effort to understand how to write a good story, I read several books about the subject. For the most part, the authors come to the conclusion that stories are about conflict, and provided a few other tidbits of wisdom. When I read game development books, the authors reiterated the same pearl of wisdom, "Stories are about conflicts". The game-development book author would often reverse the logic and conclude, that if stories are about conflict, then conflict results in a story. (I suspect game developers like to reach this conclusion because the it puts a game on a pedestal with Hollywood movies.)

Both of these conclusions are wrong. Stories are not about conflict, although conflict is an a very common technique in stories. And even if stories were about conflict, adding conflict to a game would not necessarily result in a story.

My techie brain came up with a different definition for what makes a good story, one that works on a more fundamental level. It involves three parts:

1.         At some point a potential reader must be convinced to read the story. Therefore, the story's plot must be summed up into a few sentences that are good enough to entice the reader to buy the book and begin reading.

If you look at the back of any book, you'll see a such a summary. For fantasy books, it's usually of the form: "Jelson, an unassuming Frobin, is minding his farm when one day he discovers a magical shoe that changes his world forever. Journey with Jelson as he meets up with the wizard Lootish, and eventually confronts the evil overlord, Kalziban, who a shape-shifter of devious cunning."

To a potential reader, the proper names are absolutely meaningless, so they may as well be replaced with the variables, X, Y, and Z. Once the names are removed, it's easy to see how little information is conveyed in the summary... The previous summary boils down to "X finds a magical shoe, meets wizard Y, and confronts the shape-shifting evil overlord Z." I could easily replace "shoe" with "M", since the shape of the magic item is irrelevant too.

Is this enough information to make an informed decision about the book? No.

With so little information, people's brains do pattern matching against all the other summaries they've seen and conclude, "This is just like story X, but with a shape-shifting bad guy." It is upon this "delta" over previous works of fiction that readers make their choice, along with the author's reputation. If they liked books about X, and they like the idea of a shape-shifting bad guy, they'll buy the book. If they didn't like X or can't see how shape-shifting bad guys makes for a more interesting story it gets put back on the shelf.

I find this conclusion a bit depressing, but that's the way it generally works. Consequently, a story must be a derivative of other stories the reader has experienced. If it is completely different than anything the potential reader has experienced, the summary won't convey any information at all, and most readers will put the book down. A few will persist, and they may eventually tell their friends, "Try it, you'll like it," but this will produce a very slow ramp-up in book sales.

2.         Once the reader has purchased the book, the narrative must maintain the reader's interest throughout the duration of the book. That's all; one fundamental rule. Conflict is not necessary...

Conflict doesn't hurt though, along with a number of other literary devices. For a list of some devices, see Evolutionary explanation for entertainment, The attraction of impossibility, and books about how to write stories.

Basically, a story keeps a reader's interest by introducing a number of characters designed so that the reader will quickly like and/or empathise with them. The characters' narratives are then followed by the story. The narratives are designed to place the characters in interesting situations so that the reader (as their friend) wants to hear what happens. (If the author doesn't manage to make the reader like or empathise with the characters, then the reader won't really care what happens, and won't stay interested.) The interesting situations usually involve conflict, romance, challenges, danger, etc.

I won't go into any more detail because so much is written about the subject... just don't believe it when the authors say that conflict, or any other technique is a requirement. They're not. What is a requirement is to maintain the reader's interest.

3.         After the reader has finished the story, it's distilled and shelved into long term memory. The long-term memory of the story must be satisfying. If it isn't, the reader won't purchase any more books by the author, and the reader won't recommend the book to friends. An author who doesn't get return readers or recommendations won't do well.

How does a story produce a good long term memory?

1.         The first rule is to provide the story promised by the summary in #1, along with the norms of stories such as happy endings. Otherwise, the reader feels cheated.

2.         Make sure the story produces memories worth remembering in long-term memory, preferably enjoyable ones. Long term memory tends to remember dangers and unique occurrences.

For example: I saw Bambi when I was four. The only memories I have of Bambi are his mother's death, the forest fire, thumper talking about wobbly legs, and all of Bambi's friends leaving him to go "twitterfitting" (which I saw as social abandonment). The rest of the movie need not exist as far as my memories are concerned, although if the rest of the movie didn't exist then the scenes that I remember wouldn't be as compelling, and I wouldn't have remembered them.

Long-term memory also ferrets out inconsistencies and parts of the story that seem to be irrelevant. Consequently, a good story is very tight and all parts of the story are important for the conclusion to be reached. After all, an important part of (artificial) intelligence is identifying what data is erroneous or irrelevant and tossing it out of the training set.

3.         Only make the story long enough so that the important scenes have impact, and so the reader feels like he's gotten value for money. A story that's too long will either bore the reader so much they don't finish, or be so boring that all they remember about the story is how bored they were. (How many books that your high-school English teacher assigned only left memories of boredom?)

That's it, a nice simple rule that would drive any literature major up the wall with its techie-ness. Of course, simplicity is an illusion and the devil is in the details.

If you look at my three requirements from the right angle, they almost look like a recipe for evolution:

1.         An individual animal must be attractive enough to mate. After all, "good looks" are just a biological summary of a creature's DNA.

2.         An individual animal must survive long enough to breed, just like a story being interesting long enough to get readers to finish it.

3.         A individual animal must successfully raise its offspring... A story must create good long-term memories that cause readers to buy the author's next book or recommend it to a friend.

Virtual worlds and the authoring equation

If the authoring equation is applied to virtual worlds (or any computer games), the following conclusions can be drawn:

1.         The "summary rule" causes almost all virtual worlds to become clones of one another since VW summaries must be worded so that users get the impression: "It's like Everquest, but with better graphics and more crafting." A virtual world that cannot be compared to its predecessors so easily may sell in the long run, but at first its sales will be slow.

2.         A virtual world must keep the player's attention. I have written up several papers with thoughts about this: See Evolutionary explanation for entertainment, The attraction of impossibility, Stop the buffet, Junk food entertainment, Choice, and Choice 2.

The technique of using choice as a way to keep a player's interest is important to point out because it differentiates virtual worlds (and computer games) from stories, which have no choice. If a game provides no choices, players will (subconsciously) feel like they're reading a story, and a poorly-written one at that. A choice-less game is like chocolate cake without any chocolate in it... it's a let down.

Not to sound too pessimistic, but a virtual world only needs to maintain a player's interest long enough so that (a) they recommend the world to friends (see below), and (b) they produce such strong social ties within the world that they are reluctant to leave. After that, virtual worlds can get boring, as they often do. Stories, because they are only recommended once they're completed, and because they produce no social ties, must keep a player's interest all the way through.

3.         A virtual world must also (theoretically) leave a good long-term memory. However, contemporary virtual worlds break my long-term memory rules in interesting ways.

Because contemporary virtual worlds last so long, approximately 1000 hours, players recommend the world to friends long before they're finished playing. People reading novels rarely recommend them until they have finished with the novel, and if asked about the novel part-way through they'll say, "It's good so far, but I'm not done. Ask me in a few weeks." VW players cannot provide the equivalent reply of, "I'm not done. Ask me in a year."

The real reason why players recommend VWs to friends before completing them is because they want their friends to be part of the VW, since playing with friends makes the VW experience more enjoyable. (This tendency also provides problems for a 100-hour virtual world, as written up in The anti-MMORPG. A person will probably play a VW one or two weeks before recommending it to friends. It then takes the friends another week or two before they purchase their own copy. By that point, the original player has finished 40-80 hours of content. If the content is only 100 hours long, there won't be much point recommending the VW to friends.)

A VW author wouldn't want players reviewing the VW when they're finished with it anyway... because virtual worlds have no endings, players usually stick with them until long after they get bored, which means one of their long-term memories of the VW experience is that it was boring. Players often whinge that "Virtual world X was great when I started, but then the player base changed and became full of idiots, and the live team ruined it by adding feature Y." (Usually in more graphic terminology.) Did the player really leave because of these changes, or because they became bored, and the changes are used to rationalise their decision?

Conclusion

My analysis about what makes a good story or virtual world seems a bit mercenary. I'm basically saying a good story or VW is one that manages to attract readers/players, and keep them interested long enough to form strong memories so they'll recommend the story/player to their friends.

The concept has evolutionary similarities and ties into memes, which are viral ideas. Stories and virtual worlds are just very-long viral ideas.

 

 


 

The toy room

(Back to TOC)

9 December 2004

by Mike Rozak

One of my cousins had a huge toy room with an order of magnitude more toys that my brother or I had. Whenever I visited his house, I saw the toy room as nirvana, and thought someone with that many toys would never get bored.

As an adult, I know that conclusion to be false; kids can be just as bored with 100 toys as they are with 10 toys. In fact, I've come up with some fundamental observations about toys:

1.         A toy room with more toys will provide more entertainment, but the amount of entertainment is not a linear relationship with the number of toys. Thus, twice as many toys almost always results is less than twice as much entertainment.

This law is not always true:

o    12 toys received all at once for Christmas will result in 4-8 weeks of entertainment for all the toys. 12 toys received once a month over the year will produce 1-2 weeks of entertainment per toy, or 12-24 weeks of entertainment.

o    Some toys are synergistic. 1 Lego set provides 1 hour of entertainment. 2 Lego sets provide more than 2 hours of entertainment because the pieces from each set can be combined in new ways.

2.         A toy room is more fun when enjoyed with a bunch of friends. Old toys are rejuvenated when friends stop by to play.

3.         If an adult joins in the play, toys can be kept fun longer, perhaps an infinite amount of time longer. Unfortunately, adults cannot spend all day playing (or they get bored of it), so adult-mediated play is a rarity.

The reason why adults greatly improve a toy's fun is because:

1.         They have more experience, and are able to come up with new ways of playing with the toy that children haven't thought of.

2.         They're an authority figure, and can direct the imagination a bunch of chaotic kids into more sustainable activities.

3.         Their play is selfless. Adults make play decisions designed to make play more enjoyable for the kids, not necessarily themselves.

                        Because adults cannot spend all their time playing, they sometimes create scavenger hunts, which a pre-programmed play sessions. A scavenger hunt has the kids play with a series of toys in context to provide more meaning. Scavenger hunts aren't as good as a real adult, but they're usually better than a free-form toy room.

                        When I was 12, I began programming games on the Apple ][ after I read an issue of Byte magazine that had an article about Zork, along with the Basic program for a simple adventure game. Ever since, computers have become an everlasting toy for me. I could be locked up in small room, and as long as I had a computer, I would keep myself entertained.

I don't know if a programmable computer is the ultimate toy (for me), or if at 12 years of age my brain matured to the point of being able to entertain itself.

Virtual worlds and toy rooms

As I have stated in "Virtual world as platform", I often think of virtual worlds as being collections of sub-games. Or in other words:

a sub-game in a virtual world = a toy

a virtual world = a toy room

If this is the case, then my observations about toys infer the following observations about virtual worlds:

1.         Offering more sub-games will allow a virtual world to hold a player's interest for longer, but not linearly. Twice as many sub-games do not provide twice as much entertainment, unless:

1.         The sub-games are rationed out to players over the course of time, such as quarterly updates with new features, or limiting sub-games to quests.

2.         The sub-games are synergistic: Combat is fun. An economics game is fun. If the two are combined they're even more fun.

2.         A virtual world is more fun when one's friends are around. To a lesser extent, having unfamiliar players as well as enemies also makes them more fun.

3.         A virtual world with a game master (filling the role of adult) is more fun than free-form play. Game masters work well in tabletop role-playing games, but aren't economically viable in most online virtual worlds. A few worlds have extensive game mastering, and many have occasional game mastering. Most have employees that they call "game masters", but who are really in-world product support staff.

4.         A quest (or adventure game, or offline CRPG) is the same as a toy-based scavenger hunt. Quests are not as good as having a real game master around, but they're usually better than free-form play.

5.         What is the everlasting sub-game for a virtual world? Is it the act of creating a world? I don't know.

Toy categories

Over the thousands of years that people have been inventing toys for children, they have only created a handful of toy categories, between 10 and 20 depending upon how you count. Some of the toy categories, as listed by E-toys, are: Action figures, animals and stuffed toys, arts and crafts, blocks and building sets, dolls, DVD and VHS, etc. Each category has thousands of variations. For example: Action figures can be GI Joe, superheros, Bionicles, transformers, etc.

Virtual world sub-games likewise fall into categories such as combat, economics games, chat, puzzles, etc. These two can be subdivided into thousands of variations.

I perceive several weaknesses in contemporary virtual worlds:

1.         They don't provide enough sub-games, and put almost their effort into combat. It's like a toy room with only a handful of toys. See Virtual world as platform.

2.         In general, contemporary virtual worlds all provide the same small set of sub-games. Therefore, they cannot differentiate themselves by their choice of sub-games. Instead, they differentiate themselves with eye candy, genre, and by the details of the sub-games. One virtual world will have GI Joe action figures, while another one superheros, and a third will be transformers. Yes, there's a difference, but not much of one.

3.         Most contemporary virtual worlds subscribe to the "sandbox" theory of design, which postulates that if players are provided with enough toys they will make their own fun.

Because virtual worlds don't have many sub-games and they are just sandboxes, then their only differentiator is item #2, the specifics of each sub-game.

I am always looking for ways to differentiate, so I advocate more sub-games (#1) and a break from the sandbox theory (#3), as well as unique implementations of sub-games (#2). More sub-games and different implementations are easy enough. However, to escape the sandbox, I need to have a game master, but since an army of game masters is too expensive, I need one based upon artificial intelligence.

Replacing the game master

Being a computer programmer, I try to solve every problem I come across by writing a program. Obviously, I want to write a program to replace a game master...

At its most basic, a program to replace a game master (or adult) would pull out a sub-game (toy) for the player to enjoy. After a fixed amount of time, the computerised game master would distract the player and pull out a new toy. Repeat the process. It's a simple idea that would only work if the players were utterly stupid, which they're not.

I can think of a few improvements though:

1.         Have the AI game master try to determine how much the players are enjoying the sub-game. If they're still having fun, stay with the sub-game a bit longer. If they're "looking" bored, either try a variation of the game that the players haven't seen (such as having them kill giant rats when they get bored of killing ordinary rats), or abort the sub-game ahead of schedule and try different sub-game.

2.         Intelligently determine which sub-game would be good to use next. If kids are getting bored with GI Joe action figures then pulling out a Spiderman action figure might work, but a much different toy, such as the spaceship, might work better.

3.         Find a better way to distract the change in sub-games. If kids are becoming bored with GI Joe action figures and all an adult has to go with is a flying saucer toy, then have GI Joe and friends be abducted by aliens. From there, play with the flying saucer. When the kids get bored of that, the adult can have the flying saucer drop GI Joe and friends on an alien planet with giant trees that look like lamps, or forget about GI Joe entirely and have the flying saucer be attacked by a giant space dragon.

4.         Repeat.

This is easier said than done...

Determining if players are enjoying sub-games

An adult easily knows when children get bored. The same goes for tabletop game masters. A game AI acting as an game master would find this problem much more tricky. To do this, an AI would need to:

Personally, I prefer the "ask the players if they're having fun yet" approach, but only ask once in awhile. To do this:

1.         Divide the virtual world into hundreds of quests. (Most virtual worlds already do this.)

2.         When a player finishes a quest, ask them to rate it from 1 to 5. The polling is a bit intrusive, but not that bad.

3.         Use various mathematical techniques (whose names I have forgotten) to cluster players' preferences for quests and come up with a model of what type of quests a player would like. Amazon.com uses this for recommending books, and while the recommendations aren't perfect, they're pretty good.

The selection process is a bit tricker than Amazon.com's because it needs to take into account the player's location and his character's skill level, as well as the player's overall progress in the "storyline" of the VW (if there is one).

4.         Either recommend these quests to the player, or somehow guide the player character to the quests. For example: If a virtual world learns that a player likes to rescue pet cats, it would have NPCs from the pet-cat-rescuing quests seek out the player from miles around.

Some people do not like being second-guessed though; The virtual world would need to provide an option so players could turn off their personal game master AI.

Choosing the next sub-game

In order to choose the next sub-game for the player, the AI game master needs to know:

1.         The player's frame of mind. Thus, the AI must have a "theory of mind" about what the players are thinking and what their moods are. At its simplest level, if the player is obviously getting bored of killing rats, swapping them over to killing giant rats won't be much of a change. Having them solve puzzles or sail a ship would be better.

2.         What the players' characters can handle. There's no point in bringing out the dragon "toy" if it will instantly incinerate all the players' characters. Likewise, forcing a thief PC to fight a NPC knight in an arena isn't too fair.

3.         Where the storyline of the virtual world is leading. If the storyline dictates that the players must meet up with the evil overlord's henchmen at some point, is now the right time for him to appear? (If you don't believe in storylines then ignore this statement.)

How can AI solve these problems? I'm not entirely sure, but here are a few ideas:

1.         Manually classify each quest (or sub-game), such quests with "lots of combat", or "lots of puzzles". (Or maybe ask players classify the quest type when they finish it.)

When a player finishes one quest, determine what quests the player could undertake. If more than one quest is available, try to recommend the quest that is the most different from the last quest the player completed. (Of course, if the player doesn't like puzzle quests, then don't recommend puzzle quests, even if they'd make a good change from killing things.)

2.         When players complete a quest ask them how difficult they thought it was. Plug the difficulty, along with the players' skills, into some clever AI and produce a heuristic. When considering the quest for future players, plug in their characters' abilities and guesstimate how difficult they would rate the quest. If it's too difficult (or too easy) then don't recommend it.

Some players, with the same character skill as others, may still find quests too difficult or too easy. The AI will need to guesstimate how skilled the player is (as opposed to their character) in relation to other players.

3.         The quest's recommendation score is also affected by how important it is for the storyline, and whether it must be run in order for the story to proceed.

A user may sometimes have to complete one out of two possible quests for the storyline to advance; choose the one which the player would enjoy the most. For example: To kill the evil overlord the player may need to acquire a cloak of morphing. Since all players must eventually acquire the cloak, a virtual world might provide 2-4 quests that result in the player getting the cloak, but recommend only one of them to the player.

Which quest the AI recommends next can also be affected by:

Transitioning between sub-games

You may not have noticed, but the examples I gave used quests as atomic units, not sub-games. The reason is simple:

Producing AI to transition between quests is fairly simple. Producing AI to transition between sub-games is very tricky.

To transition the player from one quest to another, the AI merely needs to (a) unobtrusively inform the player that the quest exists, and if the quest is a long ways away, (b) find a way to get the player there. Both of these problems have simple solutions, although more complex solutions would produce smoother (and less obvious) transitions for players. For example: A really clever way to get a player from point A (where they finished their last quest) to point B (where the next quest is), is to use a small quest in-between that coincidentally transports the players from A to B.

Transitioning between sub-games is much more difficult because there are more transitions; a player may transition from one sub-game to another dozens of times an hour. If these transitions are not 100% smooth, players will notice them and become annoyed at the artificiality of the system. Players will also notice transitions between quests, but because a transition is only needed once every few hours, the transition won't grate as much.

To do a proper sub-game transition system, the game master AI needs to have a huge expert-system knowledge base. For example: Lets say the AI determines that a player is getting bored with killing orcs, and decides that a car chase would be more fun for the player. How does the AI explain the transition? Do some of the attacking orcs suddenly jump into a car and take off down the road? Where did the car come from? Where do the players get their car? Can orcs even drive? What if the players were attacking a heard of wildebeests? Do they drive? What is a car doing in a fantasy setting anyways? What happens if the players decide to keep the car they pilfered?

To make things even more complicated... What happens if there are no roads nearby? Does the AI invent a road? If it does, what will players think when they suddenly see a road appear? What do players in a different group think when a road suddenly appears, and only a few moments later a carload of orcs comes hurling towards them on the newly invented road? What happens when the world is filled with too many roads?

And, that's not the end of it... What happens if the car-load of orcs, in an attempt to escape their attackers, have a head-on collision with a dragon that another group of players was attempting to kill? The dragon ends up with a broken leg and must be put down, while the orcs are all flattened and rushed off to hospital. The experiences for both the orc-chasing players and the dragon-slaying players have just been ruined.

That's not the worst: If the game world starts dealing with individual sub-games, it should conceptually tie these sub-games together into quests. (It doesn't have to, but people seem to like having overarching goals.) Creating automatic quests is fairly tricky, and not something that contemporary AI is up to, except for simple quests.

Automatically generated quests

If you read, "Automatically generated content", you'll know that I like the concept of automatically generating quests. I don't think, however, that such quests will be very interesting, and they will pale in comparison to the hand-written ones.

Automatic quests could easily be written and effectively used for transition quests, however. In the example I used above, where a quest is needed to get the player characters from the city where one quest ends (A) to the distant city where the next quests beings (B), an automatic quest would be a cinch... I can imagine a few scenarios:

Of course, players will realise that the quests are automated, but I don't think they'll mind the occasional automatic quest.

Theory-of-mind in quests

Even though only a small percentage of quests can be automatically generated, every quest can be tailored to its players by understanding what the players like:

Did I go off topic?

How did I get from toy rooms to game-master AI?

Simple really...

1.         A toy room is a source of entertainment, but it's much more entertaining when an adult selflessly partakes in the play.

2.         Virtual worlds are conceptually toy rooms. They're more fun when a game master is selflessly intervening to make the world more fun.

3.         Since game masters are too expensive to employ, an AI will have to do. (Some people may object to AI second-guessing their desires, so provide players an option to turn off the AI.)

4.         Given the lack-of-sophistication of current AI, the AI's functionality is limited to recommending quests.

5.         Important quests must be manually generated. However, simple automatically-generated quests are possible, and are particularly useful as "bridging quests" between the manually-generated quests.

6.         Even important quests can be minimally adjusted to take the players' likes and dislikes into account.

Some additional thoughts...

If several players frequently team up into parties, how does the AI handle each individual's different motivations? Are there quests specifically designed for teams vs. loners, and does the AI use its knowledge about the party?

If an AI notices two players (or small parties) that seem to have similar habits, does it try to get them to bump into one another? Is there a gender and age bias? (See Build it and they will come.) What if a player wants to play alone?

Does the AI attempt to produce conflict by encouraging enemies occasionally run into one another?

Alternatively, does the AI pretend it's Charles Dickens and have the same NPCs reappear from time to time? Some favourite NPCs from the past could be kidnapped by enemy NPCs from the past.

Do the quests, with the help of AI, attempt to intertwine the players' experiences? Maybe one quest will call for a character to steal an item from a NPC. The NPC might then choose to hire another PC to get his item back. Which PC out of 1000's is targeted for the retrieval quest?

Is the amount of PvP in a quest also monitored? Does the AI remember how much PvP a player likes and recommend quests based upon that?

How does the AI attempt to create strong memories, as mentioned in The authoring equation? Are some sub-games only brought out occasionally in order to create an extra-sharp memory of them? Or are combinations of sub-games used? Or the context? How are other players involved? Are important choices necessary for strong memories?

How are inconsistencies and extraneous quests minimised? The storyline that the player selects is one method. (See Intertwined storylines.) Are there others? Are there sub-themes to the storylines that should be emphasised for specific players?

If a player only follows recommended quests they will finish in (lets say) 100 hours. If they play all the quests in the world, play with several characters, or try several storylines, they might get 300 to 1000 hours of entertainment out of the virtual world. Does this solve the problem presented in The anti-MMORPG? Does this make the virtual world too expensive?

The possibilities are limitless... and that's the point. Adding a game master AI can significantly enrich the world. Some players won't like it; they can either turn off the game master, or go back to their sandboxes.

 

 


 

The player pyramid

(Back to TOC)

29 January 2005

by Mike Rozak

Since learning about Richard Bartle's player models (Hearts, Clubs, Diamonds, Spades: Players Who Suit MUDs), I've tried to find or invent alternative player models, not because I think categorising players as socialisers, killers, explorers and achievers is wrong, but because having only one accepted player model imposes blinders/limits on designers. It's like having a toolbox with only one tool.

In the past year, I have come up with many categorisation schemes, but this one, "The player pyramid" is particularly interesting because it not only describes why players visit virtual worlds and how the player types interact with one another, but it also implies a business model. (I don't necessarily like the implications of the business model though.)

I came up with the player pyramid when I combined disparate thoughts that I had posted in two previous articles. The first article discussed player motivations (from The dream machine and The attraction of impossibility), while the second was about different population levels in virtual worlds (from Virtual world spectrum).

For those of you who don't wish to read the previous articles, here's a summary:

Combining the two

If you peruse the list of goals and fantasies listed in The dream machine and The attraction of impossibility you'll notice that the different goals and fantasies which players wish to fulfil require virtual worlds of different population levels:

The pyramid

Categorising players by the population of the virtual world that they need to fulfil their goals and fantasies produces a pyramid. Most people are happy with linear fiction, forming the base of the pyramid. On top are those people whose needs are met by single-player games. Above those are the multiplayer gamers, game-like virtual world players, and world-like virtual world players. Each group's population is progressively smaller:

World-like VW players

Game-like VW players

Multi-player gamers

Single-player gamers

Linear fiction readers/watchers

(One reviewer pointed out that the shape isn't necessarily a pyramid, citing the popularity of MMORPGs in Korea.)

The player pyramid reveals some interesting relationships between the player types:

Players at the top of the pyramid require that there be players underneath them, or their goals/fantasies won't be fulfilled. For the most part, players on the base of the pyramid would rather not have any players above them because players higher on the pyramid detract from their experience. This is a major source of conflict.

This relationship is similar to Richard Bartle's observations that killers need to have socialisers and achievers to prey upon, but socialisers and achievers don't like the killers. Likewise, socialisers need achievers, although achievers could care less about socialisers. In the player pyramid, killers would be at the top, with socialisers below, and achievers and explorers at the bottom.

The bargain

The obvious solution to the pyramid's conflict is to create virtual worlds that caters to players on the base of the pyramid, and let the world-like players be damned. After all, the top of the pyramid is a small market that does nothing for those below them except make life difficult.

This solution is already practiced:

At first glance, all seems lost for players who wish to play in world-like worlds.

However, world-like players do have something that players lower down the pyramid want... real-world money.

It's a matter of supply and demand. If a world-like gamer wishes to have their goals and fantasies fulfilled, they must have a larger population of players below them who do not want to be inn-keepers, kings, or outlaws. In a way, these other players are part of the world-like gamer's entertainment. Even if players at the base of the pyramid don't think of their experience as being entertainment for someone else, they often resent the inconveniences imposed on them so that world-like players can have fun. Given a choice, players whose goals and fantasies are fulfilled at the base of the pyramid chose single-player games, multiplayer games, or game-like virtual worlds, not world-like virtual worlds. Money works wonders though...

One way world-like gamers can get a supply of inn patrons, vassals, or victims is to pay them to play. Of course, no world-like player is going to pay other players $10/hour to play a role. However, they will (and do) subsidise them:

Some other subsidised marketing models are possible:

Example of design and business model

The player pyramid implies some design constraints and business models for creating a world-like VW. Here is one example of how the design could be incorporated into a virtual world: (I have chosen it because it's distinctly different than Achaea's or Second Life's approaches.)

Of course, very few (if any) of the specifics of the implementation are new ideas.

Ramifications of "the bargain"

If a virtual world design-team decides to have world-like players subsidise the players below them, then there are ramifications:

Alternative business models

The player pyramid's business model isn't the only one available to virtual worlds. A couple other well-known models exist:

Conclusion

In a virtual world that subscribes to the player pyramid:

1.         Players on the top of the player pyramid (looking for a world-like VW experience) require a large base to fulfil their goals/fantasies. Those at the base (looking for a game-like VW experience) would rather not have any players above them.

2.         Players looking for a world-like VW experience (on top the pyramid) will pay for the game.

3.         Players at the base (those who prefer multiplayer and single-player games) will be able to play for free (or very cheaply), at the expense of having the inconveniences of a world-like VW imposed on them.

4.         A large number of players at the base of the pyramid is necessary to attract and keep players at the top.

5.         The main purpose of content (quests, dungeons, etc.) is to attract a steady stream of players into the base of the pyramid, those who like multiplayer and single player games. These players will only stay around for 100-200 hours, or until the content is consumed. Thus, the content must be renewed every few years to re-attract them.

6.         The player-vs-player elements of the virtual world provide interesting ways for world-like players to interact with one another and players lower on the pyramid.

 

 


 

Digging their grave

(Back to TOC)

4 March 2005

by Mike Rozak

I am in the process of designing my virtual world. One of the questions I have recently asked myself is, "What happens after I ship?"

Of course, I will need to spend time fixing bugs and handling customer issues.

But what about new features? New content? Improvements to the graphics and sound engines? (A.k.a. "eye candy"). These questions lead to an even larger one: How long can I keep a virtual world alive by trickling in new features, content, and eye candy?

I have to update my features, content, and eye candy, since without the updates my virtual world would die a fairly quick death. Once all the content was consumed by current users, they would leave and never come back. New users would appear, but, over time, the outdated eye candy would reduce their numbers. (A top-10 MMORPG with state-of-the-art eye candy has approximately 100 times as many users as a top-10 text-MUD, with 25-year old eye-candy.) And even if I did manage to keep my eye candy up to date, I'd eventually run out of people that hadn't yet visited my world.

The "ever-expanding" world

Historically, virtual worlds release frequent feature, content, and eye-candy updates: MUDs are continually improved and expanded upon by their authors. MMORPGs release update CD-ROMs once every 6-12 months, with new clients, rendering engines, 3D models, textures, and sound effects.

One would expect a virtual world to live forever if it produced sufficient updates, but they don't seem to. Ultima Online came out around 1998, peaked at around 200K users a few years later, and its numbers have been gradually declining for the last five years. Everquest released a few years later, peaked at around 400K users, and its numbers are gradually declining. I suspect the same has happened to text MUDs, although I have no numbers.

Both Ultima Online and Everquest have shipped many expansion packs to keep their features, content, and eye-candy up-to date. As a result:

Yet they still die...

In fact, they're dying at a more rapid rate than their player numbers would indicate. The number of people that play virtual worlds has been growing at 30%-50% per year, yet Ultima Online and Everquest have only managed to keep their populations constant. If their populations were growing at the same rate as the market, they would merely be "treading water". They aren't even doing that. What happens when the virtual-world market growth stalls? If new players weren't continually streaming into the market then Ultima Online and Everquest would be shrinking at a rate of 20%-30% a year!

What is going on?

Both games have deeper feature sets and are larger than the latest-and-greatest. Everquest's eye candy isn't even that far behind World of Warcraft's. (WoW's eye candy is behind the times though, and far behind Everquest II. EQ II has only half the players, partly because its eye candy requires too high-end a computer.)

Digging their own graves

Why are Ultima Online's and Everquest's populations falling, let alone failing to grow by 30%-50% a year?

Here's my theory... Every time a virtual world releases an expansion pack, the following happens:

1.         The cost of the game goes up since players now have to purchase yet another package. A more expensive game means fewer players. Companies easily counteract this by rolling the expansion packs into their main game box that new players purchase; only existing players need the expansion pack.

2.         The new eye candy increases the user base. Of course, some users don't have the hardware necessary for the the new eye candy, but on the whole, eye candy is beneficial unless it's too bleeding edge, like Everquest II's.

3.         New features are added, such as more player levels, pets, and player housing. The new features revive the interest of current players. Potentially new players, however, find new features to be a negative since they inevitably make the game more difficult to play. When too many features are added to a virtual world, the learning curve is enormous and new users shy away.

To make matters worse, the players entering the virtual world market today are less "hard core" than previous waves of new players. Thus, they dislike complex UIs and feature sets even more than the old players. So, not only do the expansion packs dissuade potential hard core players, they are a huge negative for the newer wave of non-hardcore players.

4.         The new content added by the expansion packs are enjoyed by existing players because they have already completed the old content. However, from the point of view of new players, the virtual world already had 400 hours of content before the expansion pack added another 100 hours. The virtual world now has 500 hours of content...

The problem with so much content is that the virtual world becomes an insurmountable obstacle. More content just makes it that much harder to "finish" the world.

For example, I am not a hard core gamer. I will play a game for 50-100 hours and then get bored. I end up finishing half the games I play, and leaving the other games 30%-50% from the end. If a virtual world has 400 hours of content, I'm only going to get through 25% of it, at most. Adding another 100 hours means that I'll only get through 20% of it, and will get bored soon after I've graduated from killing rats to killing giant rats. When I play a RPG, I want to be killing the evil overlord by the time I get bored, not just giant rats!

Again, increasing the amount of content is doubly detrimental because the newest wave of virtual-world players are not hard core. I suspect they have longer attention spans that I do, with most playing between 100 and 200 hours.

Furthermore, the more content a virtual world has, the more expensive it becomes to upgrade its eye candy and features. After all, if a world has 1000 different 3D models for monsters, converting to a new 3D engine that supports more polygons and bump mapping could require 1000 models to be rebuilt and retextured. Likewise, adding a new feature could easily break one of the 1000 quests a world has, requiring the testing of all 1000 quests after a new feature has been added. A virtual world with only 500 monsters models and 500 quests has a much easier time adding new eye candy.

Virtual worlds are digging their own graves when they put out expansion packs because the expansion packs gradually focus the virtual world towards an ever-shrinking class of niche players: Those players who have already spent large amounts of time in the virtual world, and new players with huge amounts of time on their hands.

However, if virtual worlds don't put out out expansion packs, they'll die an even quicker death... It's like a war movie where the bad guy points a gun at the captured soldier and makes him dig his own grave. Soldiers inevitably dig their own grave, hoping that the delay will provide enough time for plot twist to occur.

The plot twist

At the moment, I can think of three answers to the "What happens after I ship?" question:

1.         The ever-expanding world - The virtual world digs its own grave, as above.

2.         The Phoenix approach - The virtual world refuses to put out expansion packs and accepts a quick death, fading to obscurity in a year or two. This solution isn't all bad because the virtual world company can take the money it would have spent building expansion packs, and put it into a new world, with the latest and greatest features, content, and eye candy. The approach mimics the mythical, Phoenix, which is reborn from its own ashes.

The Phoenix approach is the one most commonly used by linear fiction and single-player games, with the profits from one movie/game being used finance the creation of an entirely different movie/game. I won't bother going into any more detail since it's such a common approach.

3.         Continually remodelling - See below.

A "continually remodelling" world still releases frequent expansion packs, like an ever-expanding world, but with a twist:

1.         The expansion packs would be free to all players, which inevitably means a free download.

2.         They would include new eye candy, although the eye candy would never be able to keep up with the latest and greatest.

3.         New features would be added, but other features would be removed. Introducing a ranger class that could own a pet, along with the pet feature, might cause the necromancer class to be removed. Removing existing features is needed to ensure that the world's learning curve doesn't become too steep.

4.         Likewise, as new content is added, old content would be removed. Again, this ensures that the world is accessible to new players.

This approach is more like remodelling than adding on.

In architectural terms, the ever-expanding world approach begins with a normal-sized house and continually adds onto the house, eventually producing a monstrosity with 3 kitchens and 16 bathrooms. The continually-remodelling world approach keeps the house roughly the same size, but occasionally remodels the kitchens and bathrooms to keep up with the latest styles and gadgets. The Phoenix approach tears down the house once in awhile and rebuilds a new one on the same spot.

Ramifications of continually remodelling

Continually remodelling has some important ramifications to the virtual world:

 


 

Altruism

(Back to TOC)

8 March 2005

by Mike Rozak

A recent discussion on Terranova got me thinking about altruism and its role in virtual worlds, particularly how it pertains to the world vs. game nature of a virtual world.

However, before I discuss altruism, I will spend some time "boiling down" linear fiction, single-player games, and multiplayer games into their fundamental constructs...

Boiling them down...

At its most fundamental, a piece of linear fiction (aka: story) does the following:

1.         Provide a setting and "laws of physics" that govern the world. Does the story take place in the 18th century or the 25th century? Is there magic or warp technology?

2.         Get the reader to like one or more non-player characters (NPCs) in the fictional world.

3.         Have the "liked" NPCs make interesting choices and undertake interesting actions. The choices and actions aren't necessarily interesting for the NPC though; they are designed to be interesting for the reader.

4.         Have the world, NPC, or other NPCs change in interesting ways as a result of the NPCs actions. Again, the change must be interesting to the reader.

One of the ways to make the change more interesting is to make it believable but unexpected. A good story teller never gives the reader exactly what they expect.

5.         In a story, all the changes lead the "plot" to its final conclusion. Those choices and changes that are not needed to reach the conclusion are generally removed from the story.

6.         Each story has a theme, which is the ultimate message of the experience, such as "You walk though history and are part of future histories." The theme permeates the work.

The fundamental elements of a single-player game are:

1.         Provide a setting and "laws of physics" that govern the world. This is the same as with a story, although setting and physics seem to be more important in a single-player game than a story... Could this be because the other elements (see below) aren't handles as deftly by contemporary game designers? Or, are world and physics inherently more important to a single-player game?

2.         Get the player to identify with his player character (PC).

Unlike linear fiction, games do not usually try to get the player to like any NPCs; Artificial intelligence isn't sophisticated enough to make likeable NPCs. At best, cut scenes or other pre-programmed scripts are used to make NPCs likeable, but the effect pales compared to linear fiction.

3.         Provide interesting choices and activities for the PC to undertake. The choices and actions are mainly designed to be interesting to the player, not the player's character.

An activity might be interesting in a game but not a story: Real-life hiking is an interesting activity to partake in because it requires mental concentration to avoid slipping, tripping over rocks, and being bitten by snakes. It is dreadfully boring to read about though.

4.         The world, PC, and NPCs change in interesting ways as a result of the PC's actions. Generally, changes occur only after the PC correctly performs a specific action, like pressing the correct button, killing a monster, or saying the right phrase to a NPC.

5.         Single-player games have conclusions and plots, just like stories. The player's choices, activities, and their results are designed to lead to this conclusion.

Players make an unwritten agreement with the author that they won't try to subvert the built-in plot (too much) if the author provides them with an enjoyable experience. Of course, in most games, it's impossible for a player to subvert the plot beyond refusing to advance it.

Occasionally, a single-player game allows for several different conclusions, letting the player's choices affect the outcome. There are never more than a few possible endings though, so in reality, the player has very little choice in the matter.

6.         Some single-player games have themes, although most are the same chiche, "You too can save the world."

Multiplayer games are like single-player games, except that other players perform some of the roles that NPCs do linear fiction. A multiplayer game does the following:

1.         Provide a setting and "laws of physics" that govern the world. Setting and physics seem all-important to multiplayer games. Again, could this be because virtual-world authors don't know how to use the other elements? Or is it a fundamental difference?

2.         Get the player to identify with his own PC and to make friends with other players. Again, NPCs are not yet sufficiently intelligent enough for players to like them.

3.         Provide interesting choices and activities for the PC to undertake.

4.         The world, PC, other PCs, and NPCs change in interesting ways based on the players choices and actions.

Change that results from player to player interaction is believable, and often unexpected. Change resulting from PC to NPC interaction is sometimes unbelievable, and usually too predictable. Thus, the changes brought on by player to player interaction are (usually) more satisfying, and have fewer pre-programmed limits. However, "interesting" also implies "appropriate to the world", which is unlikely, since most players don't role play.

5.         Multiplayer games don't seem to have plots, at least for the moment. This is a strength and a weakness.

It is a strength because some players don't like making an unwritten agreement with the author. It's a weakness because many of the player's choices and actions lead to naught. In a story or a well-written single-player game, all choices and actions have a purpose.

I suspect that as multiplayer games try to attract a more mass market audience, they'll need a plot. See The End.

6.         Theme? What theme? Most multiplayer games don't have a real theme. Themes are particularly difficult to maintain over long periods of time and with thousands of players trying to subvert the theme. Does this mean that virtual worlds can't/shouldn't have a theme?

You may be reading some of the observations I made about multiplayer games and thinking, "That's wrong. They don't really work like that!". You're right. A typical MMORPG divergs from my predictions:

1.         Setting and physics are everything, to the point where most MMORPGs seem to have little else.

2.         MMORPGs don't do enough to get players to meet other compatible players. This is easy enough to remedy.

3.         They provide very few choices, and very few varieties of activities. See Choice, Choice2, and Virtual World as Platform.

4.         The player's character and other player characters can be changed by the player's choices and actions, but the world and NPCs are generally static.

5.         Contemporary MMORPGs include quests, which are "mini-plots". Personally, I like quests, but I don't know if they work well in the grand scheme of things. I sometimes wonder if quests are vestigial remains from single-player games.

Example: Early written fiction was often written as epic poems. Poetry was used in bardic tales, the precursors to written fiction, as a memory aid and because music was played with narration.

6.         Themes, if they exist at all in a MMORPG, are incorporated into the quests.

Altruism and NPCs

From the "Boiling them down" section, it's obvious NPCs are necessary for linear fiction and single-player games. Without NPCs there would be far fewer interesting choices and their effects, and little plot or theme. Without NPCs all you have is a world, and the laws of physics that govern it.

However, a multiplayer game (theoretically) doesn't need NPCs at all. Everything that a NPC does can be accomplished by a PC, but much more intelligently and believably. After all, NPC AI is so poor that NPCs are limited to mindless roles like cannon fodder.

NPCs are not replaceable, and provide a vital role in multiplayer games:

1.         They always stay in character, adding to the believability of the world as a whole. Very few players role play.

2.         A NPC works 24/7. Players are only logged on an average 20 hours a week (with more mass-market players on for less time). While it's possible to get players to work in shifts to fulfil a role, they each bring their own personality and knowledge to the role, making for an inconsistent experience... "Why are there 18 different shop keepers for one store?", or "Why does the shop keeper have 18 different personalities?"

3.         NPCs are designed to be altruistic. Some players are altruistic too, but most are self-centred...

Most players make choices and undertake actions that are fun or beneficial for themselves. Some will uncaringly destroy the enjoyment of other players just for their own enjoyment. A few will purposely destroy another player's enjoyment.

NPCs are designed by the author to be altruistic and make the game fun for the players. Even in cotemporary MMORPGs, where NPCs are incredibly stupid, NPCs are still designed for the players' entertainment. A monster is designed to be easy to kill, and not to run away to save its own life, nor to call in all its buddies within shouting distance.

A virtual world can be created without NPCs, but there are consequences:

1.         If few players are logged on, a NPC-less world is desolate and boring. This causes players that log on to quickly log off, which ultimately creates a feedback cycle that produces an empty world.

2.         The virtual world becomes very dangerous, with self-centred players running around in groups killing one another. NPCs could do the same, but their altruistic programming forces them to stay in a portion of the world appropriate to their level of difficulty.

3.         To make the world less dangerous, authors provide rules of engagement and safe zones at the expense of allowing players' actions to have interesting effects. Dark Age of Camelot only allows PvP combat in certain areas and only against official enemies, creating a everlasting battle with no possible victory and no real consequences. World War II Online has official enemies, but doesn't limit where the combat occurs, creating a more dangerous game where players' actions have consequences. World War II Online has fewer players than Dark Age of Camelot... Is this a consequence of the heightened danger?

4.         A virtual world can be designed to completely eliminate danger and (hopefully) attract mostly altruistic players. I believe this is what A Tale in the Desert does. In the game, players try to cooperatively build a civilisation. There is no combat, and (as far as I know) no sanctioned PvP of any kind. A Tale in the Desert doesn't attract many players though... Is this because it's designed for more altruistic players, a rare breed?

World War II Online and A Tale in the Desert both attract a small but enthusiastic group of players. I suspect the reasons for this is that they also allow players to change the world, something which other MMORPGs do not allow. Most players don't seem to care if they can change the world, but some do, enough that they're willing to accept a higher danger level or to exist in a world where they must cooperate with one another to achieve anything.

Interestingly, neither game relies on NPCs for entertainment, contrary to most MMORPGs. Does this mean that the more NPCs in a world, the more static it will become?

The opportunity costs of NPCs

NPCs are beneficial to a virtual world because they're altruistic. However, their existence lead to static worlds, which means that players' choices and actions ultimately come to naught.

The reasons why NPCs result in a static world are obvious:

1.         Interesting NPCs (quest givers, shop keepers, etc.) are a lot of work to create, and authors can't afford for their work to be lost just because a player decides to kill them or otherwise make the NPC irrelevant.

For example: It takes a bit of work to create a NPC that hands out a quest to "kill all the orcs on the other side of the hill" to players. If the quest-giving NPC is killed, then players may never know to attack the orcs on the other side of the hill. If the orcs are all killed, then the quest-giving PC is out of a job. Either way, the world can't afford to change.

2.         Uninteresting NPCs (monsters) are easy to create, but must still be spawned. Ideally, players should be able to make a concerted effort and kill all the orcs in the world to be rid of them, permanently changing the world. However, if all the orcs are killed, what do players do for entertainment?

MMORPGs prevent all the orcs from being killed by continually spawning orcs in one area of the world. Unfortunately, this leads to a static world since nothing the players can do will eliminate the orcs. The orcs' altruistic programming that prevents them from leaving their spawning region makes for an even more static world.

3.         Quests (which are a by-product of NPCs) also ensure that a world is static. They too take effort to create, and authors don't want to waste the effort once the quest has been completed by just one player.

I have heard of several solutions to this problem:

1.         Get rid of NPCs altogether. World War II Online and A Tale in the Desert do this.

As described previously, removing all NPCs has its own problems.

2.         Improve the artificial intelligence behind NPCs and the code that creates NPCs. Make sure that if all monsters are about to be killed off, at least some manage to escape. Allow non-monster NPCs to react to their environment and make intelligent decisions. Furthermore, let NPCs create their own quests based on their goals and needs.

Even though AI can be improved beyond what is seen in contemporary MMORPGs, AI has its limits. The world will still be static, but on a higher level of abstraction than contemporary MMORPGs. The NPCs and quests will appear to be dynamic, until players realise that the same quests and NPC templates keep reappearing in different disguises. Furthermore, the AI must be designed so that it's "altruistic" but not a pushover, however one does that.

I haven't seen this implemented.

3.         Hire human GMs to continually tweak the world, spawning NPCs where they see fit, and providing some higher-level intelligence for NPCs. Unfortunately, this can become very expensive.

Volunteer GMs would work too, as long as they remain altruistic. Giving ordinary players power over NPCs poses a problem, since the controlled NPCs would lose their altruism and fail to fulfil one of the reasons they're in the world.

A major problem exists with human GMs: The players must believe that the GM is impartial. As soon as the GM shows favouritism the players will revolt. The same rule applies to AIs, but it's easier for players to believe that an AI is impartial.

Wish was going to implement such GMs, but it was cancelled during beta, citing low player numbers. Again... Does the mass market player want to change the world?

4.         All of the above. An author can reduce the number of NPCs (per player) to create a more dynamic world but more dangerous world. AI can be improved. Payed GMs or altruistic volunteers can guide the AIs.

Encouraging altruism in players

Despite all I've written, player's aren't completely self-centred. Virtual worlds often try to encourage altruism:

1.         They encourage players to become altruistic role players, by guiding them towards acceptable behaviour and rewarding them with experience points or loot. Most MMORPGs do so already by handing out experience to players that kill monsters, but their efforts might be directed more effectively.

2.         Multiplayer games provide tools so that altruistic players are welcomed and provided more influence. These include constructs such as guilds and mentors.

3.         Some virtual worlds award role-playing points (related to altruism) for players who are doing a good job role playing.

4.         Another possibility is a world-design that attracts altruistic players. I'm not exactly sure how to accomplish this.

A quick summary

To sum up what has been covered so far:

1.         A virtual world without enough altruistic PCs and NPCs becomes dangerous, and attracts a smaller audience. Free-for-all PvP MUDs don't have too many players.

2.         The danger can be removed or reduced with hard-coded rules, but then players are left in a less-interesting world that is often static. Ultima Online originally had open PvP, but was ultimately forced to impose PvP restrictions. In Dark Age of Camelot, the PvP restrictions produce a more static world; none of the three realms can ever win the PvP war.

3.         Although altruistic players are rare, altruism can be encouraged with the proper tools and using appropriate rewards. Role-playing points, guilds, and mentors are a common technique.

4.         NPCs, which are (always) programmed to be altruistic, can counteract the PC's self-centred-ness. Interactions with NPCs are pale imitations of interactions with real players though. Furthermore, the more NPCs in a world, the more static the world.

5.         Improving NPC AI can reduce the amount that NPCs constrain world change and make the world less static. Improved AI also makes the NPCs more interesting to interact with.

Consequently, a safe and interesting (mass-market) world that doesn't rely on altruism is static. (Such as World of Warcraft or Everquest II.)

So what?

What's the big deal if NPCs aren't intelligent and a virtual world is static? Most players don't seem to mind not being able to change the world; Everquest (I & II) and World of Warcraft own much of the US market. In all three worlds, players run around, undertake lots of action, and pretend that they are changing the world even though the static nature of the worlds is obvious.

Obviously, I can't argue against the fact that most players don't seem to mind static worlds. A few thoughts do arise though:

1.         Players didn't mind sprite-based graphics when their computer could only produce sprite-based graphics. They got "spoiled" by 3D-accelerators and now expect more. They might do the same for static vs. dynamic worlds.

2.         Mass-market virtual worlds may be able to get away with a static environment, but niche worlds might require dynamic worlds to compete. The trend already points strongly in that direction.

3.         Applying Richard Bartle's player model: Achievers and explorers, who are more world-oriented, are also more interested in dealing with altruistic NPCs. Socialisers and killers would rather interact with players, who are more interesting than NPCs. Socialisers are altruistic, while killers are self-centred.

4.         Looking at the player pyramid in this light reveals that players at the bottom of the pyramid are happy with existing AI, while those at the top require more complex, human-like AI. As AI improves, players at the top of the pyramid will have less and less need for players below them.

5.         Damion Schubert's response to the player pyramid is a version where altruistic players occupy the top layers. My player pyramid assumes more self-centred players would impose their needs on players below, requiring that players at the top of the pyramid financially subsidise the experience of those at the base, if they wish to attract any players to be a base. If altruistic players could be found to fill the top layers, the altruistic players would actually improve game-play for those at the base and a different business model could be used.

 

 


 

Differentiation

(Back to TOC)

9 March 2005

by Mike Rozak

I have been concerned about how much one virtual world can differentiate itself from another. The virtual worlds out there (MMORPGs and MUDs) and not too promising; they're almost all variations on the Tolkien or D&D fantasy theme.

If virtual worlds can't differentiate themselves then the market will ultimately collapse into a few games, just like the market for office software only has a few competitors out of the hundreds of office products that once existed. After all, two word processors can only be so different.

Points of differentiation

Worlds have several broad categories that they can use for product differentiation:

Differentiating multiplayer features

When I look around existing virtual worlds, I notice an amazing lack of differentiation amongst the multiplayer features. The standard multiplayer features are:

Even the specific implementations of the features aren't that different from world to world...

Such lack of variation worries me, because if multiplayer features can't be differentiated sufficiently between worlds then "multiplayer" virtual worlds may not be differentiable.

Since existing virtual worlds make poor role models, I decided to come up with my own list of multiplayer features: First, I wrote down a list of reasons why a person would chose to play a multiplayer game over a single-player game. Then, I listed some ways that the features could be differentiated from one another. Below is the list:

Reason for playing multiplayer game

Ways of differentiating

Be part of a group.

  • The ease of finding a group.
  • The ways that being part of a group aid game play.
  • The ease of communicating within the group.

Change the world. (This is a multiplayer feature since few people seem interested in having a private world that they can change as much as they wish.)

  • How much players are allowed to change world.
  • What tools are available for change.
  • The politics of getting change improved.

Compare rank with other players.

  • Level ladders, obviously.
  • Different types of "badges" or "titles" acquired during play.
  • The ability to host competitions.

Gossip and hearing about what other players are going.

  • Tools for transmitting gossip.
  • Tools for acquiring and organizing gossip.

Hang out with friends.

  • Tools commonly used by friends to meet up and communicate must reflect the flavour of the world.
  • How a world deals with level-disparity amongst players in a party.

Entertain other people with poetry, song, dance, music, acting, artwork, or writing.

  • The tools available affect how well the entertainment urge will be satisfied.

Experiment with different personalities (Richard Bartle's Hero's Journey)

  • How the world encourages players to experiment with different personalities.

Griefer

  • A world probably doesn't want to attract griefers, although various features could be customised.

Lead people.

  • Sub-games on offer in which the player can lead other players.
  • Tools for making leading easy.
  • Politics.

Learn a new language.

  • How the world encourages players with different languages to intermingle.
  • What languages the virtual world attracts.
  • How the world makes it easier for those just starting out to learn the new language.

Meet new people.

  • The sub-games (and the rest) of each virtual world attract different personality types to the world. This, in turn, affects the type of people players will meet in a world.
  • How the world encourages role playing, brings out the person's "real" personality, or encourages discussion affects the type of people that are met.
  • The easier it is to meet people, and of specific kinds, the better.
  • Virtual worlds could have shards specific to real-world geographic worlds so players could meet one another offline.
  • The ways that players can get together and socialise in the game.

Mentor or teach people.

  • Tools for finding students.
  • Different tools for mentoring.

Organize people.

  • The activities in the world that require someone with organizational abilities (such as raids).
  • The tools that make it easier to organize people.

Playing games against human opponents is more challenging, fun, or rewarding.

  • The sub-games that allow for player-vs-player interaction must be differentiated.

Practice for real-world encounters with people. This could include getting over shyness.

  • The available tools should provide a wide array of choices.

 

 


 

Intertwined relationships

(Back to TOC)

16 March 2005

by Mike Rozak

The more I try to boil down the virtual world experience into a set of simple constructs, the more intertwined the constructs become. This document describes some of the intertwined relationships that seem to arise.

Technology

At the lowest level, virtual worlds are based on technology, such as:

Modern virtual worlds almost all have the same technology categories, although different virtual worlds emphasise some technologies over others.

Sub-games and activities

The technologies are combined together to create sub-games and activities such as:

The better the technologies available, the "better" the sub-games can be. An equivalent sub-game can still be written with poorer technology, but it seems to attract fewer players. (For example: Text adventures are much faster to write than graphical adventures, but graphical adventures attract more players. The same goes for text-based Rogue/Hack RPG games, and graphical equivalents like Diablo II. The difference in the experiences is mainly that the graphical adventures/CRPGs have a much better graphics engine.)

Sub-games are combined to form other sub-games:

Not only are sub-games combined together, but they vary slightly by location (space) and time. For example: The combat sub-game varies geographically because players fight lions in the savannas, and cave trolls in the mountains. Trade also varies because some goods are cheaper and more readily available in some places than others.

Sub-games can vary substantially from game-to-game. A puzzle in Zork is significantly different than a puzzle in Myst.

Synergy between sub-games

A collection of sub-games is combined to produce the final "game". What sub-games are used and how they're combined has a lot to do with the aggregate experience of the game.

Combining some sub-games create synergies that greatly improve the overall experience: Single-player CRPGs almost always combine the combat, trade, quests, and dungeons sub-games. These sub-games seem to go well together, just like peanut butter and jelly.

Some sub-games create anti-synergies that weaken the overall experience: Puzzles and card games are rarely found in single-player CRPGs. Combat is rarely found in adventure games. (Garlic is good in pasta sauce, but doesn't work well as an ice cream flavour.)

Of course, the synergies and anti-synergies are all in the eye of the beholder. (Some people like garlic ice cream, at least theoretically.)

Single-player games tend to be based on just one sub-game, while massively multiplayer games tend to include as many sub-games as possible, including crafting the kitchen sink. I suspect this occurs because, as I'll discuss later, different sub-games attract different player types, who in turn have their own synergies that counteract the anti-synergy of too many or dissonant sub-games.

Furthermore, some sub-games are more fun as single-player experiences, while others are more fun with multiple players. The "chat" sub-game is very boring when there are only AI's around to talk to (aka: Eliza), but enjoyable with other people.

Consequently, single-player sub-games seemed to be grouped into "games" of the following genres:

Massively multiplayer sub-games seem to combine best into:

Sub-games attract different single-player gamers

Every player has their own set of likes and dislikes for sub-games. Some players like combat, while others prefer puzzles, and yet others prefer both. If you grouped players by what types of sub-games they prefer, I suspect that you'd end up with the standard game genres listed above. After all, 30 years of trial-and-error has shown which genres (combinations of sub-games) sell best.

A mixed-genre game, which doesn't have all the sub-games expected of a genre, or which extraneous sub-games, results in a weaker game and fewer players.

Just remember though, that genres are quick ways to label large groups of players. They are only vaguely correct, and no matter what combination of sub-games are chosen, a sub-set of players will enthusiastically like that specific collection of sub-games. The group may be very, very small though.

Sub-games attract different multi-player gamers

As I wrote up in Differentiation, players are attracted to multiplayer games because the multiplayer games fulfil a need that single-player games do not. Some people play MMORPGs because they like to compete against other players, as opposed to AIs. Other players like MMORPGs for socialisation. Etc.

If you create a chart that lists the different motivations for players wishing to play multiplayer games, compared to the sub-games, you'll see that some multiplayer motivations are best fulfilled by some sub-games.

In the following chart I list a few motivations and a few sub-games. In each cell I have placed a capital "X" where I think that players with the motivation would really enjoy the sub-game, a lower-case "x" where there is some appreciation, and a blank where the motivation can't be fulfilled by the sub-game. If you don't agree with my sub-games, motivations, or X's, then create your own graph; the principles remain the same:

Chat

Combat

Trade

Quests

Puzzles

Card games

Spacecraft

Hang out with friends

X

X

x

x

x

X

x

PvP

X

X

x

x

x

X

Role playing

X

x

x

x

Be part of a group

X

X

x

X

x

Rank/competition

x

X

X

x

x

x

Entertain others

X

Griefer

x

X

x

Leader

X

X

x

x

x

Meet new people

X

x

x

X

x

X

The specifics of the chart are less important than the general conclusions:

Each motivation interacts with other motivations

Since players inhabit the world together, they affect one another's experiences. A world whose sub-games attract armies of griefers, will in turn scare away socialisers, whose experience is negatively impacted by griefers. (Richard Bartle pointed this out in his player models.)

I have created a chart of my guesstimate of these relationships using the previously-listed motivations. Each row shows the list of motivations, and the columns indicate how much players of the row's motivation are affected by players with the column's motivations. (Thus, the column for griefers shows negative or neutral results for all other players since no one likes them, not even other griefers.) Each cell is filled with "++" for a very positive effect, "+" for a slightly positive effect, blank for neutral, "-" for slightly negative, and "--" for very negative. Again, if you disagree with the motivations or their effects, change them to suit yourself.

The "warm bodies" columns means that the player doesn't care what motivates the other player so long as the other player is around. "Warm bodies" also include players in the virtual world who really want a single-player experience. Griefers and entertainers, for example, find the the more players in the world, the better. (See The Player Pyramid.)

Warm bodies

Hang out

PvP

Role play

Group

Rank

Ent.

Griefer

Lead

Meet new

Single player gamers

-

+

+

--

Hang out with friends

++

+

+

+

--

+

PvP

++

+

Role playing

+

+

++

++

-

+

Be part of a group

++

--

++

Rank / competition

+

++

+

-

Entertain others

++

+

-

--

Griefer

++

-

-

-

--

Leader

++

-

-

Meet new people

++

+

+

--

++

Example interpretation of graph: People visiting the world who would really rather be player a single-player game dislike having other players around because they detract from gameplay. Role players and entertainers could add to the entertainment value. Griefers are a strong negative because they are the antithesis of what single-player gamers are looking for, a safe and predictable experience.

Example interpretation of graph: Players who like PvP combat (or other sub-games), like to have other PvP players around to compete with, as well as players who are interested in their rank, since they'll accept a PvP challenge. Unlike most players, PvP players don't mind griefers since griefing is a demented form of PvP, which the PvP players are willing to partake in.

Example interpretation of graph: Griefers want lots of warm bodies around to grief. They don't like players that hang out together or in groups because they're too difficult to harass; isolated prey is much easier. They don't like PvP players who will fight back. And, they especially don't like other griefers because too many griefers scare away all the prey.

Again, the specifics don't matter, but some general conclusions are important:

One of these days I'll write a small program to simulate all the player interactions. This would allow me to predict what would happen if I added or removed a sub-game.

Unfortunately, too many constants must be guessed. (I'm sure you disagree with at least half of my X's, +'s, and -'s). The effects of changing a constant are so non-linear, that a simulation would probably produce inaccurate results.

So what's the point of this document if using it as a template to design a modeller won't work?

Intertwined relationships

Some conclusions can be drawn...

Changes to one part of the system affect all the other parts:

1.         Changing a technology affects all the sub-games based on it.

2.         Changing sub-games effects sub-games which are based upon other sub-games, such as quests and dungeons.

3.         Changing, adding, or remove a sub-game affects what kind of single-game players are attracted to the world.

4.         Changing sub-games also effects how well they fulfil multiplayer motivations, and what kind of world-like players are attracted.

5.         Changing the demographic mix of multiplayer motivations produces a feedback cycle (positive and negative) which affects what players are attracted to the world.

Furthermore:

There are also implications for mass-market vs. niche-market worlds:

1.         Mass-market worlds emphasise eye-candy "technology", while niche-market worlds don't have the money for the eye candy. Consequently, they emphasise other technologies, such as AI.

2.         Mass-market worlds tend to dumb-down the sub-games as well as minimise the number of sub-games to keep the UI simple. Furthermore, mass-market games minimise the number of sub-games because they're too expensive to produce with the appropriate eye candy.

3.         Fewer sub-games in mass-market games means that quests and dungeons, which are based on sub-games, aren't as "good" as those in niche-market worlds.

4.         Fewer sub-games results in a narrower set of player motivations being met by the sub-games. In turn, fewer player motivations results in fewer players of motivations being attracted to the world.

In ecological terms, mass-market games are like savannas. They are a relatively simple ecosystem of player motivations. Niche-market games are more like rain forests, with complex ecosystems of player motivations and sub-games. Most of the Earth is covered by savanna and other simple ecosystems, while most of the Earth's diversity is in its rain forests.

5.         Mass-market worlds probably won't even try to meet niche-market player motivations because the market is too small.

6.         Mass-market worlds will be more game-like, while niche-worlds will be more world-like.

 

 


 

Text vs. graphics

(Back to TOC)

6 April 2005

by Mike Rozak

For awhile now, I have been considering what advantages text MUDs have over graphical MMORPGs. (... Mainly because the authoring system that I'm creating is a hybrid text/verbal and graphical system, so it can take advantage of text's strengths.)

Books vs. movies

Before exploring the newer medium of virtual worlds, I thought I'd see how linear fiction handles the dichotomy between text (books) and graphics (movies).

Strengths of books over movies:

Weaknesses in books (compared to movies):

Other people's opinions about the strengths of text MUDs

In his book, Designing Virtual Worlds, Richard Bartle lists the following ways that text MUDs are better than graphical MMORPGs:

A few months ago I posted a user poll on www.MudMagic.com, asking "Why do you prefer MUDs to MMORGs?" (MudMagic is a text-MUD web site.) I only had 10 slots, so I limited the choices to the following:

Smaller community

5.65% (24)

Role playing

18.82% (80)

More innovation

9.65% (41)

More imaginative worlds

10.35% (44)

MUDs have deeper gameplay

23.53% (100)

Blind; I use a screen-reader

3.53% (15)

Cheaper

12.24% (52)

Don't need expensive PC

3.76% (16)

MMORPGs don't like my OS

3.53% (15)

I prefer MMORPGs

8.94% (38)

Some respondents wrote additional comments about why they prefer text-MUDs, including:

  • The types (maturity?) of players attracted to text-MUDs are different than those attracted to MMORPGs.
  • Closer interaction with the design staff (due to the smaller communities that text-MUDs attract).
  • Text-MUDs often allow players to change the world.
  • The ability to play from work. (This would allow players to use the text-MUD as more of a chat room, or to monitor the progress of events throughout the day.)
  • One's imagination is better than graphics.

The web-page for this non-scientific poll is http://www.mudmagic.com/polls/view/31.

From the MUD-Dev mailing list:

Some of my own thoughts

I came up with my own list: (I have eliminated those items duplicated above.)

 


 

Fun Factors

(Back to TOC)

6 April 2005

by Mike Rozak

What makes a game fun? Raph Koster's latest book, A Theory of Fun, proposes some ideas which I tend to agree with.

I thought I'd different approach to the problem: Look at some computer game categories and identify the defining "fun" characteristics of the category.

Scanning through some game review web sites, I came up with the following game categories:

MMORPGs/MUDs also come in a few different varieties:

What makes it fun?

What makes each of these categories of games fun?

The social aspects of MMORPGs and MUDs also translate into elements of fun:

Main elements

If I boil down the main elements, I come up with:

The social reasons for playing are:

Many of the elements of "fun" are opposites, such as inductive and deductive reasoning. If I combine opposites together, the list now looks like this:

 

 


 

Player Powers

(Back to TOC)

6 April 2005

by Mike Rozak

I thought I'd spend some time stating some obvious facts about the powers that players are given to affect a virtual world...

Player powers

Players have the following basic powers in a virtual world:

Notice that character control, influencing player characters, and influencing NPCs are online activities that must be done by a person real-time. Creating static content, scripts, and AI can be done offline, and affect non-specific players at a later point. I'll reference online and offline activities later.

Player categories

Virtual world "players" fit into the following categories...

The matrix

For any virtual world, it's possible to create a matrix of player powers vs. player categories. Each cell can be filled with the specific abilities that a player has. (By the way, Richard Bartle categorizes worlds based on persistence vs. player category in his book, Designing Virtual World.)

Author

GM

Trusted players

Ordinary players - Friends

Ordinary players - Enemies

Ordinary players - Neutrals

Character control

Influence other players

Table-top

PvP

Influence NPCs

Create static content

Create scripted content

PvE

Creation

Create AI

I haven't bothered filling in this matrix for a specific game. I did colour the cells and label them; see below.

Interestingly, if you examine a game and ask "Where does the fun come from?" you'll notice some trends:

Some other observations:

 

 


 

Virtual world equation

(Back to TOC)

6 April 2005

by Mike Rozak

As I stated in The authoring equation, computer graphics thinkers have come up with an all-encompassing mathematical equation that explains 3D rendering. Most of the computer graphics lighting techniques that you may have heard about, such as radiosity, global illumination, ray tracing, motion blur, etc., are just approximations to the full solution described by the rendering equation.

Since I like to examine the underlying principles of ideas, I keep wondering if there's a similar all-encompassing "equation" that explains virtual worlds...

Here's my latest attempt.

What a virtual world is...

A virtual world is...

1.         A virtual place simulated on a computer.

2.         The place is populated by various "players" visiting the world. The most common type of "player" is an ordinary player. Other types of players exist though, including the author, members of the live team, and trusted players. (See Player powers.)

3.         Each player has the ability to control their avatar, affect the world, and affect other players, either directly or indirectly. The type of control depends upon the player type. Authors, for example, have much greater ability to change the world than ordinary players.

4.         Players visit the world because they want something out of the experience.

The most common "desire" is entertainment from the author's content, which the author created using their authorial player-powers. Most players also want to play with friends, or meet new people. Some want to run inns. Others wish to be kings. Many want to dominate others. The author and live team "play" (in part) because they wish to earn real-life money.

The players' desires are conscious, sub-conscious, and maybe even something unknown or unwanted to the player when they first begin playing, but which is ultimately "good" for them (such as Richard Bartle's "Hero's journey"). Does the the author's choice of what is "good" for the player mimic the "theme" of a novel? (More on this in a future writeup.)

To state the obvious: If the player's desires didn't somehow involve other players, the players would probably be playing a single-player game instead of dealing with all the headaches of the Internet and other players.

5.         When players try to fulfil their desires, they often annoy other players in the process. A player fulfilling his desire occasionally benefits other players.

For example: A player who wants to role play is annoyed by non-roleplayers, and vice-versa. A player who wants to grief annoys just about every player he meets. A player who wishes to be an innkeeper and chat with his customers annoys those customers who just want to buy a beer and get back to adventuring.

Of course, not all players cause each other grief, and some get along quite well; A player who wishes to play Florence Nightingale is appreciated by wounded fighters. A player that wishes to to sing bawdy songs at an inn is usually welcomed by the inn's visitors, although some customers may disagree.

How much one player's desires affects another player depends upon the individuals and their particular moods and circumstances. Some people may not like being healed by Florence Nightingale, while a small minority of others might actually enjoy being griefed. Annoyance can be conscious and sub-conscious.

See The player pyramid and Altruism.

The equation

Looking at virtual worlds in this light makes "the equation" to solve fairly obvious... The goal of a virtual world is to:

Sustainably maximise the fulfilment of the players' desires (aka: conscious and sub-conscious enjoyment of the VW), while minimising the amount that players get on each others' nerves. (To make matters more difficult, players getting on each other's nerves sometimes leads to fulfilment of the players' conscious or sub-conscious desires.)

Of course, this is easier said than done.

Some obvious solutions

Some solutions naturally follow:

1.         The author and live team need to produce the "entertainment" that players consciously and sub-consciously expect from them. Such entertainment is usually in the form of hard-coded content, scripts, and AI... what most people would call the PvE game. Some virtual worlds (see below) rely on the live participation of the author and live team, while others have no PvE content whatsoever. (See Player powers.)

2.         The world should be designed to attract "complimentary" player desires. These could be self-attracting desires, A <-> A, such as players looking to meet people wanting to meet other players wanting to meet people. They could be mutually beneficial relationships, A <-> B; Players who wish to be leaders must be in worlds with players who wish to be led, and vice-versa. Or, there can be more complex relationships of A -> B -> C -> A. See Intertwined relationships.

The player pyramid discusses one possible relationship, where some players pay the real-life bills in order to have the other players around.

3.         The virtual world's advertising should do its best not to attract players that won't fulfil their desires in the world. Attracting the right players is also important, but is (in many ways) secondary to not attracting the wrong players. If the wrong player visits a virtual world they will not enjoy their experience and will tell ten friends how lousy the world is, just like people who see a movie they didn't like. Unlike movies, players who aren't enjoying themselves will also make life miserable for the virtual world players who are playing and enjoying the world. (Note: Similar behaviour does happen in movie theatres... Occasionally, a disgruntled movie viewer will provide a running criticism of the movie to everyone sitting in neighbouring seats.)

4.         The virtual world's advertising should attract a "balanced" distribution of players. If more players wish to be innkeepers than there are inns, some of them will be unhappy. Damion Schubert pointed this out in his response to the player pyramid.

5.         The world should attract and empower altruistic players. Some people are naturally friendly and altruistic, and are a net benefit to a virtual world. The virtual world should attract these players and give them powers to maximise their beneficial effect. Other players (griefers) annoy virtually everyone else and should be kicked out. (See Damion Schubert's response to the player pyramid.)

6.         The powers provided to various types of players should be designed so the players can attain their desires. If a world attracts players who wish to lead, the world should provide tools/abilities so the players can actually lead. A world that attracts players who want to be innkeepers should provide player-run inns. Alternatively, an option for "looking to group" ensures that players that wish to meet other players can easily do so.

The powers might be dependent on other players, so that player A cannot fulfil his desires with his own powers, but must convince player B to use his powers.

7.         Players should have a choice about what they want to do, and who they typically interact with. This allows players interact with people that help fulfil their desires, and avoid annoying ones.

8.         The world should naturally funnel players to meet other compatible players. Guilds ensure that leaders and followers meet up. Likewise, PvP players are given their own PvP region of the world so they don't kill a PC that doesn't want to partake in PvP. In some worlds, fantasy races with strong personality associations attract people of like-personality into the same region of the world. (Giving players exactly what they want isn't always best though. See below.)

9.         Players are rewarded for fulfilling the desires or other players, and penalised for being annoying to other players. Players naturally reward and punish one another through in-character actions, such as as insults, avoidance, and PvP combat.

Role playing virtual worlds sometimes provide each player with role-playing points that he/she can award to another player that he/she thought did a good job role playing. The points could just as easily be handed out for "making my experience more enjoyable". Negative points could be awarded for "being a jerk"... so long as the true jerks aren't allowed to label other people as jerks and their friends as enjoyable, or the world will turn into a world of jerks, which is fine if that's what you're after.

Rewards can be simpler than this; in the case of a virtual world with player inn-keepers, making goods less expensive at the player-run inn than the NPC-run inn acts as a reward for players who visit the player-run inn.

10.      Role playing points and/or real-life cash payments can be accumulated and used to "purchase" potentially negative abilities. For example, if a player accumulated enough role playing points they could become king, which would allow them to impose their will (to an extent) on other players. Inevitably, such an imposition would be a minor annoyance to a large number of players. To become king, and thereby mildly annoy large numbers of players, the player had to do good deeds, either by getting lots of role-playing points or by paying real-life bills, so on the whole they make the world a better place to live in... This system acts like the inverse of Hindu theology, where the positive/negative actions of this life affect what you're reincarnated as. Instead, the positive actions of this life allow you to act negatively in your next life.

Notice the semi-interchangeability of role-playing points and real-life money. Does this mean that virtual worlds which are expensive to run (ones with plenty of eye candy) will value wealthy contributors above those who earn role playing points, and vice versa?

11.      Worlds with democratic player politics have a similar effect. A player who desires to be elected will only be elected if he makes enough other players' desires come true. Unfortunately, democracies inevitably lead to factions where 51% of the players are getting their desires met, and 49% are unhappy with the current administration. Role playing points used to purchase kingdoms could produce similar outcomes.

Notice that these solutions almost all require that the player makes a conscious choice, which means that the player will tend to fulfil their conscious desires and often fail to heed their sub-conscious or "good to have" desires.

Examples

Applying this model to contemporary virtual world genres (partially) explains why they succeed:

If this model is applied to single-player games, as shown at above, then you'll see that a single player game is a world with one player and an author in absentia. The absent author is limited to hard-coded elements, scripted elements, and artificial intelligence. The author "learns what the player desires and dislikes" by providing the player a choice of play styles in the guise of races and classes, as well as choices throughout the game.

A story is 0-player world in which the reader has no input. The author of a book or movie can only use hard-coded elements; scripts and AI are not possible in books. Furthermore, the author cannot even ask the viewer what they're interested in, so instead, the author designs the content to satisfy a stereotypical reader/viewer. (Theoretically, a piece of linear fiction on a computer could ask the reader what type of experience they want, and use scripts and AI to tailor the fiction.)

A more complex solution

If you have a hammer, all the world's problems look like nails...

All of the "obvious" solutions are already used in contemporary virtual worlds. Let me propose a more complex (and robust) solution that may be a bit controversial:

Create an AI (or members of the live team) whose job it is to figure out what a player desires, and hook him/her up with other players (or content) that fulfils the player's desires, while also fulfilling the hooked-up players' desires.

In other words,

Create a god/director for the world whose purpose is to make the world fun based upon its knowledge of each individual. Much of what the god/director does is act as a "dating" service, arranging "chance" encounters between players (or content).

Basically, the AI's job is to maximise the fulfilment of desires while minimising the amount that players get on each others nerves. For example: It needs to get chatty players to visit the inn run by the chatty innkeeper, while steering rushed players to a vending machine.

This isn't any easy task, and isn't likely to be solved anytime soon.

The first problem is finding out what the player desires:

1.         If you ask players what they want, they will be able to state their conscious desires. They won't be able to tell you their sub-conscious desires, and won't have clue about unknown desires that would be "good" for them.

2.         Players may not be able to put their desires into words or settings that an AI can understand.

3.         Players may lie about their desires.

4.         Players' desires change from day to day, hour to hour, and minute to minute. A virtual world can't possibly ask players what they want every 10 minutes. Polling the player once every few weeks might work, since the player's desires probably fluctuate around a mean.

5.         Some desires should not be fulfilled... Even though players may think they want to fulfil a desire, they will be unhappy if they actually do.

It's the old saying, "Be careful what you wish for. You might just get it."

Or, to paraphrase Frank Herbert in his Dune series: Most people will tell you they wish to know the future, but what they really want is to know which lottery number to pick and how to avoid accidents. They don't really want to know everything that will happen to them.

Somehow, the AI (or live team) needs to watch a player's actions and reactions, and guess what the player is trying to get out of the virtual world experience... I said it wasn't doable in the near term.

Furthermore, the AI (or live team) must determine what annoys the player so that content and encounters with potentially annoying players can be minimised. Attaining a list of "annoyance" factors comes with the same difficulties as determining the player's desires.

Once the AI (or live team) knows a player's desires and annoyances, it needs to steer the player to the content or players that will be most compatible. An even more complex solution would have the AI create content designed for the player, just like a table-top RPG GM does.

If this wasn't complex enough for an AI, the AI will probably need to be very subtle about its machinations. Many players will resent an AI playing matchmaker, even though they might appreciate the results. If the players know they are being manipulated, or in what specific ways, they might rebel and purposely try to subvert the AI. Griefers will find all sorts of ways to subvert the system. For example: If a griefer is given a quest that's obviously designed to benefit another player, this provides the griefer with information about what the other player wants, and can be used by the griefer as a weapon against the other player.

Table-top RPG's successfully implement this more complex solution because the GM (acting as the AI) only deals with four to six players, and knows them well enough personally that he can deduce their desires and dislikes. The players know such machinations occur, but accept it because the GM is their friend, and is trusted so long as he doesn't go too far.

Even though its a long way off, the idea is not that far fetched... Amazon.com uses data mining and analysis to produce reasonable (but imperfect) book recommendations based on what books you have already ordered. When I log onto Amazon.com, it recommends that I purchase a collection of books about 3D rendering and game design, along side a Thomas the Train electric train... I have ordered Christmas gifts for my nephews through Amazon.com, and the AI isn't intelligent enough to realise that the gifts weren't for myself.

 

 


 

Theme in virtual worlds

(Back to TOC)

6 April 2005

by Mike Rozak

Stories typically have a "theme", such as "If you work hard you'll succeed," "Don't accept apples from strangers," or "Never trust a man that sells you magic beans." Personally, I like themes. The MMORPGs and MUDs I've played don't seem to contain any theme other than, "Kill enough monsters and you'll grow up to kill even stronger monsters."

How does one add theme to a virtual world?

The problem

If I wanted to add a theme to a story, I'd do the following:

1.         Have the protagonist (or secondary characters) make decisions and act upon them.

2.         The results of the actions would be adjusted to agree with the theme.

1.         If the character chose well, something positive would happen to the character or his friends/family.

2.         A poor choice would produce the opposite result.

For example, the theme of Herman Melville's "Moby Dick" is that "Revenge (against a large white whale) is a bad idea." As the book progresses, Captain Ahab increasingly shirks his job as manager of the Peaquod, whose responsibility is to kill whales, get their oil, and make money. He becomes more obsessed with Moby Dick, and ultimately brings himself, the ship, and its crew to ruin.

Another example: I can imagine a book in which the protagonist changes religions, such as from Christianity to Hinduism. If the theme of the story were "Stay true to your roots," such a change might result in ostricization from the character's family, friends, and society. If the theme were "Follow your heart," bad things would happen to the character until he followed his heart and changed religions.

In a virtual world, the author can give a player the option of taking revenge, or of changing religion, but the author can't control the outcome very well. For example:

1.         The author can't cause anything bad/good to happen to the player's NPC friends or family.... Not only does the PC not have any NPC friends or family, but if he did, he wouldn't really care what happened to them. Even if the player did care, the plot device would only work once.

2.         There can't be any significant world-changing consequences because, due to the nature of virtual worlds being shared by thousands of players, the world can't be significantly affected by any single player. A single-player game could have consequences, but not a player-vs-environment virtual world.

3.         The author can't guarantee that a bad choice will lead to bad consequences. The choice can lead to negative probabilities, but then players will (rightly) whinge that they've been given a choice between two options, and one option is inevitably inferior. (See Choice.)

4.         In a player-vs-player or player-with-player world, a player's choices could have all sorts of interesting (and relevant) effects on other players. Unfortunately for the theme, its up to the other players to decide how to respond. The author has no input, so any attempt at the author's version of theme is tossed out the window. (One of the reasons that players like PvP worlds is that their actions have consequences.)

Rethinking theme

While virtual worlds are horrible at tying specific results to actions, they're great at algorithmically calculated results, and very good at having choices change the "physics" that the player character experiences.

In the Moby Dick example, the author can't guarantee the loss of life, ship, and crew, but the author can design the world's "physics" such that:

1.         The PC needs collect so much whale oil or he won't be hired as captain again.

2.         Moby Dick is a nasty whale that has a good chance of destroying the ship, along with the PC and crew.

3.         The PC gets a non-monetary reward (XP?) for killing Moby Dick. XP may not be necessary as a motivation, since the satisfaction of getting revenge might be player driven, not PC driven. However, if the author specifically designs in the choice, "Kill Moby Dick or make money," then both choices must have, on balance, equally good/bad outcomes. Since killing Moby Dick is more dangerous than killing lots of smaller whales for their oil, killing Moby Dick must provide some additional reward. It can't be money though, since choosing to collect whale oil returns a monetary reward.

The choice is then left up to the player. There are no guaranteed outcomes though.

Alternatively, in the religion scenario:

1.         Different NPCs may be friendlier towards PCs of one religion or the other. Choosing the change religions means that NPCs that used to like the PC may not, but other NPCs may suddenly like the PC. It's up to the player to decide how to deal with the change.

2.         PCs following the Christian religion may have different restrictions and abilities than those of the Hindu religion.

Likewise, the player can make the choice and experience the results of their choice. A guaranteed negative outcome for one choice, while possible, will cause the player to (rightly) complain.

I chose the change-religion theme because it's very similar to the choices a player makes about his character's race, class, armaments, etc. In a virtual world, many important choices don't lead to positive or negative consequences for the player. Instead, they result in changes to the "physics" under which the player's character operates. A fighter can wear armour and wield large weapons, while a magic user must forgo weapons and armour, but can cast spells. The fighter and wizards exist under different physics regimes. A fighter attacking with a sword experiences slightly different physics than one attacking with a mace since a sword works better against plate armour, while a mace is more effective against chainmail.

An author can impose a theme on the virtual world by controlling the specific consequences that result from the player's choice. The consequences must balance out, or it won't be a valid choice. (Obviously-idiotic choices excepted.) (See Choice.)

A world that wishes to make a point about religions would allow the player to select a religion, and make the difference between religions severe. For example:

1.         NPCs are more or less friendly to PCs based on the PC's religion.

2.         PCs have dietary restrictions based on religion, that pose minor but frequent problems.

3.         PCs have religious duties, such as attending Church or praying toward Mecca five times daily. Can PCs do anything but worship on their holy days?

4.         How does a PC's religion affect the character's resurrection after being killed?

5.         How does a PC's religion affect their conduct in the world? In combat? In economics? (In medieval Europe, Christians weren't allowed to charge interest, but Jews were, which is why they became bankers.)

6.         What special abilities does a PC get from his religion?

7.         Etc.

What I propose is different to what contemporary virtual worlds offer. Even though they let the players make choices about their race, class, guild, and sometimes religion:

1.         Contemporary virtual worlds rarely make the consequences of a choice severe enough to make them noticeable. For example: Classes are watered down so they're similar. Even if fighters aren't allowed to cast magic, so many magic items are handed out that it makes little difference. Magic users are archers who shoot fireballs instead of arrows. Etc. Ultimately, one class is pretty much interchangeable with another. Races are even more identical, and only vary in their appearance.

2.         Contemporary virtual worlds tend to use the same consequences as one another. Fighters in world A have similar advantages and disadvantages to fighters in world B. The same with magic users, clerics, etc.

A thematic world might model fighters after medieval knights, and only allow them to initiate an attack against other knights or heathens, and require that they rescue all damsels in distress. A thief (or brigand) could attack anyone, but wouldn't have the weapon training, and would be disliked everywhere. A different thematic world might model fighters after Samurai, or Massai warriors, including their skills and belief systems.

Reality vs. fantasy

This brings up another point. MMORPGs err on the side of fantasy and "low impact"; MMORPGs give players the experience that the players expect, and don't try to add any surprises. They don't want players whinging that their magic user can't attack a monster using a sword, or that their fighter can't use magic. Consequently, and over the decades, they water down the restrictions until there is very little difference in the choices that players make, and very little possibility for "theme". The world loses its "flavour" and becomes like McDonalds' "Spicy Chicken Burger", which isn't at all spicy.

My preference is to err on the side of reality, or at least "flavour". A fighter should be a very different experience from a magic user, cleric, or thief. Being a dwarf should be very different than being an elf, human, or halfling.

As long as reality is not too onerous, some doses of reality should be thrown into the experience. If a player wishes to own an inn, fine. However, make them maintain a store room full of goods, hire NPC employees, deal with repairs after bar fights, attract patrons, etc. Or, if the player chooses a religion, they must experience some of the sacrifices that come with the religion. Adding a bit of unexpected reality turns the game into a learning and thinking experience, which is what a theme is all about. Without a theme, the experience is just empty, tasteless calories, a kind of "McWorld".

"Good-for-them" desires

In Virtual world equation, I talked about virtual worlds fulfilling players' desires. Some desires are conscious and some sub-conscious. I also brought up the point that some desires might not be known to the player, but might be "good" for them. One example is Richard Bartle's "Hero's journey", where players use the anonymity in virtual worlds to experiment with different roles and determine who they really are.

Stories frequently embed "good-for-you healthy bits" in their sugary entertainment. For example, if you read Moby Dick, you're going to learn a lot about the whaling industry of the 1800's, whether or not you wanted to. Upton Sinclair's "The Jungle" is a social documentary about immigration and poverty. Even a medieval fantasy novel will contain some truths about the middle ages.

Virtual worlds can also include "good-for-you healthy bits"... They can encourage players to:

 

 


 

Experience points

(Back to TOC)

6 April 2005

by Mike Rozak

I mentioned role-playing points in Virtual world equation, and neglected their identical twin, experience points...

Traditional MMORPGs and MUDs reward experience points (XP) to PCs whenever they kill monsters or complete quests. The origin of this goes back to pen-and-paper Dungeons & Dragons.

The fictional justification is that the more monsters your PC kills, the more skilled your PC becomes at killing them. In other words, practice makes perfect. This makes sense.

The game-play reason for experience points is that they're used to advance the PC to its next level, or the PC's skill to its next level. Levels are a useful game-play device for the following reasons:

I agree with using levels (class or skill) as a gameplay device. However, in my opinion, awarding XP for killing monsters is a stupid thing to do. (I stand virtually alone here; Almost all MUDs and MMORPGs award XP for killing monsters.) Here's why I think it's stupid:

Role playing points

XP should be handed out when players do their part in making the world are more-enjoyable place for other players. This can be done by:

Conclusion

I like to boil down a problem to "first principles". Sometimes the "first principles" my thought experiments produce agree with current practices. Sometimes they disagree.

In this case, my thought experiment has produced first principles that vehemently disagree with MMORPG's contemporary use of experience points.

Is my thought experiment correct? Could 99% of all MMORGs, with millions of happy users, really be wrong?

Awarding experience points for killing monsters seems to lead to "the grind", which is prevelent in 99% of all MMORPGs. Awarding experience points for behaviour is common in roleplaying MUDs, so I suspect it leads to more roleplaying, and to an experience in which "players are part of the artwork". Most players will probably resent this; role playing text-MUDs, after all, are a very small market.

 

 


 

The game with a thousand faces

(Back to TOC)

17 April 2005

by Mike Rozak

To state the obvious, a standard MUD/MMORPG has the following features:

This ubiquitous list appears in virtual every MUD/MMORPG "feature list" web page, along with their documentation's table-of-contents. The implementation specifics change from game to game, such as what races or spells are available, but not the generalities. (Some MUDs/MMORGs don't even have different races and spells, relying on the same humans, elves, dwarves, halflings, and fire-ball spells.)

In abstract "game" terms, this means that most MUDs/MMORPGs are essentially the same game, but with different window dressings. In his book, "The Hero with a Thousand Faces," Joseph Campbell says the same about many hero myths. Hence, the title for this article; While there are thousands of MMORPGs and MUDs, they are really the same game with different faces grafted on.

I found such homogeneity disturbing, so I spent time searching for different "genres" in descriptions of the thousands of existing MUDs and MMORPGs. It seems that a handful of genres covers almost all MUDs and MMORPGs:


I have labelled these sub-genres because it's possible to produce one virtual world and customise the shards of the world to produce different sub-genres. For example: World of Warcraft has mostly PvE shards, some PvP shards, and a few role playing shards. Aficionados of a sub-genre will point out that failure to specialise in a sub-genre produces an inferior experience; Many MUDs/MMORPGs specialise in only one sub-genre.

The "Standard MUD/MMORPG" genre attracts the bulk of the players (80%-90%), with PvE worlds being the largest sub-genre.

When I asked about this issue on the Mud-Dev mailing list, several people replied that other genres undoubtedly exist, but that they haven't yet been discovered. This could be because of:

Looking at the problem in a different way

Since I couldn't find other genres, I thought I'd do a thought experiment to bypass my near sightedness...

I asked myself: What fundamental technologies are used to produce a MUD/MMORPG? The answer:

These statements are generalised... Text MUDs don't use graphics, and many MMORPGs are still based on 2 1/2D sprites. However, 70%(?) of all virtual world gamers are using 3D accelerated engines, and this percentage continues to increase. Likewise, not all virtual worlds have artificial intelligence, sound, a scripting language, persistence of the world, or the ability to seamlessly transfer new content/software. The trend, however, is for their inclusion.

Now that I know what technologies are used to make a virtual world, my next question is: What genres of entertainment can be produced using such technology?

To use an analogy: A book is made from paper and ink. An origami crane is made from folded paper (and perhaps some ink). An origami crane is obviously not a book. However, a pop-up children's book has the properties of both an origami crane and a book, so it is a book? Most people would agree that it is a book. Without stepping "outside the box" to a different level of abstraction, pop-up children's books would never have been invented. Can different virtual world "genres" be discovered using a similar technique?

Existing single-player game genres

Once a developer has developed the technology necessary to produce a MMORPG, the following single-player game genres can "easily" be created from the same technology: ("Easily" is a relative term.)

Some genres require different technologies and can't be easily produced from a MMORPG's components:

What else can be done?

I brainstormed some other games (and entertainments) that could be created, using essentially the same technology that's present in any MMORPG. These ideas are half-baked, and most won't work. They do illustrate some possibilities, and (at the very least) a different way to approach the problem:

But are they really "virtual world" genres?

Some of the ideas I described are virtual worlds, but most don't subscribe to any commonly accepted definition of "virtual world", even if the definitions are stretched beyond recognition; they are not virtual worlds. If it's any consolation, they do use the same fundamental technology that's needed for a virtual world.

If you can get past the fact that virtual world technology is being used to create something that is not a virtual world, you face another hurdle: Undoubtedly, most of the ideas won't work. One or two of the ideas might succeed, and might provide a welcomed alternative to the standard (and cliche) MMORPG formula that's used today. Unfortunately, I can't tell you which ideas will work, if any.

Even if none of the ideas work, the approach of trying to build something new with the technology pieces from a MMORPG might prove fruitful.

 

 


 

The parlour, the lobby, and the sand box

(Back to TOC)

26 April 2005

by Mike Rozak

In my continuing quest to discover alternative genres to the standard virtual world, I have come up with another approach...

The parlour

The most basic virtual world is a chat room, so I'll begin there.

Imagine that you create the ultimate chat room. It has every conceivable chat feature that you'd want in it, including voice-chat, E-mail, BBS, rendered avatars with full emote abilities, and a traversable world with fantastic scenery and sound effects.

Unfortunately, as we all know, players will eventually become bored with the chat room. Often times, players will get in a "rut", hanging out with the same friends, and never meeting anyone new. (Meeting new people is difficult and somewhat stressful.) The same old friends night after night eventually becomes tiresome.

To make meeting new people easier, and to alleviate some of the boredom with existing friends, the chat room adds social games, such as cards, checkers, and maybe even chess.

Players eventually get bored of these social games, and want an continuous supply of new games that they can use for entertainment, socialising with friends, and ice-breakers to meet new people.

At this point, a chat room can take a variety of approaches to the game problem, with two opposite poles:

1.         It can add sub-games where a group of people chose to enter the sub-game and can subsequently exclude new entrants. Card games and board games all act this way.

In virtual world terms, this turns the chat room into a "lobby". Players meet in the lobby, and then go into their own private rooms to play sub-games. The rooms' walls might be transparent, letting other players watch, but players still have the right to control who is let into their sub-game.

2.         The sub-games could be playable by all players in the world. Players don't need to sign up to play, nor can they be excluded. Everyone is in the same game; in virtual world terms, this extreme is a "sand box".

The sand box

If a virtual world consistently adds sand-box style sub-games, it eventually turns into a standard MUD/MMORPG, with features such as: Guilds, PvP combat, PvP trading, creation, housing, crafting, resource collection, etc. (Note: I'm intentionally leaving out quests; I'll get to those later.)

When a lot of players are placed into a world, their desires and activities often conflict with one another. Role players will get annoyed with non-role players. Only one guild will be able to slay the dragon of uber-loot when it spawns once every two days. Twenty players will want to fill the one slot allocated for a king. Etc. Basically, desirable resources are scarce and contested, whether they're dragons, kingdoms, inns, or intangibles like being top on the character ranking.

The virtual world designers (or some algorithm) must figure out which players get to use/control the resources, and hence, which players are able to fulfil their desires and goals. The mechanism that decides which players get preference is very important, and how much the mechanism favours a player affects how "powerful" the player is.

Players realise this. Once they have determined a goal/desire to fulfil, players try to acquire the power necessary to overpower other players competing for the same resource. Basically, they do what it takes to be successful, whatever their definition of success is.

Many years ago, I concluded that to be successful (in real life), you need luck, skill/intelligence, and/or hard work. (The definition of success varies from person to person.) Thus, if someone wishes to be a millionaire, they can win the lottery, be smart enough to invent a best-selling gadget, or work 16 hours a day for the rest of their lives. A combination of two or three of the factors improves one's chance of success.

Designers need to decide what factors will make a player a success, and consequently, how power is achieved and goals/desires fulfilled. The following mechanisms are possible:

I have been using the term "power" a bit too loosely. I have treated "power" as a one-dimensional value, but it actually represents a very complex and highly-dimensional comparison. The same problem exists in physics, where the term "energy" is conveniently tossed about, despite there being many forms of energy: kinetic, potential, heat, etc. Energy can be converted from one form to another, but the conversion is always lossy. Likewise, "power" can be converted from one form to another, but it is always lossy because there are many different forms of power, and one player's valuation of one form of power is different from another player's.

My intuition tells me that a world should allow luck, skill/intelligence, hard work, and altruism to be factors capable of achieving power. Maybe even real-life money should play a role. If this is the case, then contemporary MMORPGs are severely deficient on their use of luck and altruism, and nearly as deficient in skill/intelligence.

Some other tricks can be used to make the power flow more efficiently, and thus produce a better game.

The lobby

The opposite approach to the sandbox is to allow people in the chat room to enter their own private worlds and play games with one another. These games can be any computer game playable by a small group of people. CRPG and adventure games are the most obvious sub-games, but auto racing, flight simulators, space combat, and real-time strategy games are also possible.

If the lobby just acts a gateway to traditional computer games, why would a player subscribe to a lobby-like virtual world and not just buy the stand-alone games individually?

Why would a player wish to partake in a lobby-world instead of a sandbox?

What are the advantages for the author?

What are the disadvantages?

In-between... instanced dungeons

What's between a lobby and a sand box? A world with instanced dungeons, like GuildWars.

The difference between GuildWars and the lobby I described above is that nothing goes into or comes out of the sub-games in the lobby. When a player starts a lobby sub-game, a new character (appropriate to the sub-game) is generated with all its equipment, and is destroyed (along with the equipment) when the sub-game finished. GuildWars allows players to bring their own characters into a sub-game along with their own equipment, and likewise, changes to their character or equipment is brought out of the sub-game.

An instanced dungeon is a sub-game where players can bring characters and equipment in and out.

The advantages of players being able to bring their characters and equipment into the sub-game are:

The disadvantages are:

In-between... quests

The next step up from instanced dungeons are quests and areas of the world that are typically only visited by one group at a time. Quests are semi-private sub-games that other players can barge in on, if they wish.

Quests have the following advantages over instances dungeons:

And the following disadvantages:

Conclusion

This thought experiment has provided a few interesting results:

 

 


 

Why traditional MUDs/MMORPGs work

(Back to TOC)

19 May 2005

by Mike Rozak

This writeup is a rehash many of the thought experiments I've had in the past year, and explains why (I think) traditional MUDs and MMORPGs "work". If you haven't read other parts of my search for other genres, you might wish to do that now.

The quest

Before I talk about traditional MUDs and MMORPGs, I'll explain why single-player CRPGs work. However, to explain why CRPGs work, I first need to explain how quests work, since CRPGs are basically a series of quests.

A quest is:

1.         A series of sub-games, such as combat, travel, searching, puzzle solving, and talking with NPCs. The sub-games should be reasonably fun in themselves. (See Raph Koster's book, "A theory of fun".)

2.         The sub-games are tied together by a narrative that explains why the specific sub-games are to be played, and which often explains the order they're played in.

3.         The narrative provides a motivation for the player's character to undertake the quest, such as "To rescue the princess."

4.         The quest also provides a motivation for the player, such as "Find the sword +5 of demon slaying."

Combining a series of sub-games together into a quest, and providing a reason for the sub-games to be played, introduces some story into the world., and makes the experience more fun. Quests also prevent players from overdosing on any particular sub-game to the point being sick of it.

Single-player CRPGs

Single-player CRPGs are basically a series of quests that ultimately lead to a grand finale.

CRPGs work because:

1.         Quests are fun.

2.         The variable reinforcement scheme of experience points and loot encourage play.

3.         As characters level up (and become more powerful), the sub-games are changed with the addition of new skills, magic spells, items, and monsters. These changes prevent the player from getting bored.

4.         Character levels and meta-quests provide goals that go beyond just finishing the shorter quests.

5.         Other sub-games appear when the players get loot and experience points from quests, such as resource allocation.

6.         CRPGs provide a sense of advancement, self-worth, completion, and escapism.

7.         Plus, they're a great time waster.

Traditional MUDs and MMORPGs

Traditional MUDs and MMORPGs "work" because:

1.         They're CRPGs, and are successful for the same reasons that CRPGs are successful.

2.         MUDs and MMORPGs allow people to play with their friends, or meet new friends.

3.         Because thousands of players exist in the same world, new sub-games appear. These include trading, crafting, castle sieges, ship-to-ship combat with a crew, etc.

4.         Competing against other players is more challenging, and much less mind-numbing that computer AIs.

5.         In CRPGs, players kill monsters and collect loot so they can level up, get new skills, and fight new monsters... which basically results in interesting changes to the sub-game just as they were getting bored. MUDs and MMORPGs also take advantage of this connection between level and evolving the sub-games.

However MUDs and MMORPGs also use monster slaying for the "grind", along with crafting and some other sub-games. The grind is a mechanism that determines how much power a player will have relative to (and to be used against) other players. Basically, the more work a player puts into their character, the more power they have, and the better able they are to outrank other players and fulfil their desires.

6.         MUDs and MMORPGs provide ways for players to wield their power and fulfil their social desires. These include: PvP combat and various rankings (alpha-male syndrome), running guilds, controlling cities, organizing events, playing politics, etc.

7.         Players vary in how much they care about the social (power) aspects of the game. Some just want a single-player game or just want to play with friends. Others have desires that can only be met by having other players around. See The player pyramid.

To handle this dichotomy, many traditional MUDs and MMORGs create two games in one. The first involves lots of quests and is basically a single-player CRPG that can be played with friends. The second layer, often referred to as "the elder game", is about the power aspects of the world. Those players who are not interested in the power plays (or long-term socialisation) leave when they run out of CRPG content. The elder game won't work without non-power players around, so players interested in the elder game must play in worlds with single player CRPGs. They simply find a way to get through the CRPG aspects of the game as quickly as possible. Often this includes buying in-game power with real-life currency.

Why adventure-game virtual worlds don't work

In The trouble with explorers, I tried to determine why Uru Live failed. The culmination of all my thought experiments (to date) provides the following explanation.

1.         Adventure games are also based on quests. However, adventure-game quests use a large variety of puzzles instead of a few sub-games. CRPGs repeat the same sub-game over and over again. Adventure games try to make every sub-game unique.

2.         The problem with puzzles (of the adventure game variety) is that once they're solved, they're solved. They can only be played through once, and their solutions are quickly posted on web pages.

This characteristic has many ramifications for a virtual world...

3.         CRPG combat is more fun when played with a group of friends, or against real people. Solving adventure game puzzles with friends it not necessarily more fun, and often it's less fun, since one player seems to come up with most of the solutions, leaving the other players as dead-weight.

4.         If player A plays alone and solves all the puzzles in a quest, he cannot replay the quest with player B (who has never played it), because A already knows the solutions to all the puzzles, and has to spend his time trying not to look impatient.

5.         Since puzzles are inherently about the player's intellect, and not about something the player's character knows, adventure games don't include "levels", losing one of the devices that make CRPGs fun.

6.         Because the solutions to adventure game puzzles can be posted on the web, they cannot be used for "the grind", and thus can't be used to control which players have power. Without a device to acquire power, the elder game isn't viable. If another device is added for the elder game, such as resource gathering, then the two games become completely different and disjoint; they might as well be in completely different worlds.

Basically, adventure games don't become any more fun when played with friends, and there's no way to use an adventure game as a base for the elder game. You can create a virtual world based on an adventure game, but players will play through the content in 20-50 hours, mostly in isolation, and then leave because there's no elder game.

Note: Puzzle Pirates includes puzzles, but is not an adventure game. It uses a handful of puzzles to create "the grind", instead of relying on the combat sub-game. Other than the substitution of sub-games, it follows the same formula as traditional MUDs and MMORPGs.

Some thoughts to take away

A traditional MUD/MMORPG is written to the following formula:

1.         Figure out a set of desires that players have which can only be met by a virtual world with thousands of other players. Not surprisingly, these desires are usually social in nature, such as running a guild, being number one in a player ranking, or being able to beat up on other people. (The top of the player pyramid.)

2.         Create a game (or activity) that is fun to play single-player, and even more fun to play with a group of friends. The game must also withstand Internet latency and bandwidth, and satisfy condition 3. Ideally the game should lead to new sub-games (like economics) when used in a world with thousands of players. (The bottom of the player pyramid.)

3.         Figure out how to use the game as a mechanism to determine how much power players at the top of the pyramid get. Players use their power to out-rank other players to control resources they need to fulfil their desires. Thus, produce "the grind", where the player with the most time (or money) on his hands wins.

4.         Produce a win-win relationship that binds the top of the pyramid to the bottom. The top of the pyramid must offer something that the bottom wants, and vice versa. In many virtual worlds, the players at the top subsidise play for players at the bottom.

Some virtual worlds seem to put more emphasis in #3 and skimp on #4, or vice versa. For example, a traditional MUD/MMORPG is almost all #3. Second Life is almost all #4. I'm not sure why this tradeoff exists.

 

 


 

The fun in genres

(Back to TOC)

19 May 2005

by Mike Rozak

In my continuing quest to find alterative genres for virtual worlds, I recently had a bit of an epiphany... or at least came up with a different way of looking at the problem...

Genre = what makes it fun

Don't define genre by the code functionality, as I did in The game with a thousand faces, but define genre by what makes the virtual world fun.

Wielding this definition, I came up with the following genres:

Primal

The virtual world allows players to fulfil their primate and animal urges that aren't met in real life. Virtual worlds are particularly good at fulfilling socialisation urges/needs, such as friendship, belonging to a tribe, gossip, rivalries, child rearing, and being the alpha (fe)male.

Worlds: Traditional PvP and PvE worlds rely heavily on generating fun from primal desires.

Games: Single-player games do not do well at fulfilling primal urges. Some do make an attempt, such as virtual pets substituting for child rearing.

TV: Soap operas and (melo)dramas such as "Sex in the City" fulfil primal urges.

Reptilian

Primates evolved from reptiles, which have even more basic drives. An occasional hit of adrenaline and violence is fun. Food and sex could also be included.

Worlds: Violence is standard fare. Adrenaline is difficult due to Internet latency.

Games: First person shooters, sports games, racing games, and combat flight-simulators

TV: Horror films, some sports broadcasts

Comedy

Comedies do very well on TV and at the movies. Comedy doesn't seem to translate well into games or virtual worlds though.

Worlds: Is this possible?

Games: There have been a few attempts (mainly as adventure games), but nothing too memorable.

TV: Sitcoms, comedy shows, romantic comedies

Inductive

These worlds require that players do the same thing over and over again, with variations on a theme. The repetitive action keeps the player in familiar/safe territory, and the variations require just enough mental energy to make the game interesting. In many ways, this type of activity is similar to what humans have been doing since the beginning of time, hunting and gathering... which is the main sub-game in most MMORPGs.

Worlds: Traditional MUD/MMORPG

Games: Most computer games

TV: Sports broadcasts, formula children's shows like Power Rangers

Cool new stuff

Movies have an unofficial (and critically derided) genre, the "Special effects movie". For the most part, people see SFX movies because they're visually (or acoustically) new. I don't know if this rates as a virtual world genre, but it might.

Worlds: Any world with the latest eye candy.

Games: Any game with the latest eye candy, such as the Myst series.

TV: Special effects movies, especially science fiction like Star Trek and Star Wars

Creation

Players enjoy creating their own objects, or otherwise changing the world.

Worlds: Second Life

Games: 3D rendering packages, mod editors, machinima

TV: Home-improvement shows

Deductive

Fun comes from encountering entirely new sub-games and learning how they work. Each sub-game requires a different solution.

Worlds: Uru Live (now deceased)

Games: Adventure games like Myst

TV: Mystery shows

Experience (and exploration?)

The world tries to portray an authentic experience for the player to partake in.

Flight Simulator (not a virtual world) is an excellent example. The "fun" of Flight Simulator is having all the real-life controls of Cessna or Boeing 747 in front of you, and flying the simulated air plane. This is different from the "fun" of a combat flight simulator, where the game doesn't try to be realistic, just adrenaline packed.

Worlds: Role-playing worlds and historically accurate worlds.

Games: Flight simulator

TV: Some science fiction like 2001, some period dramas, nature shows

I ordered the list according to my best guess at the genre's market size, for TV, games, or virtual worlds. For example: Commercial television is loaded with shows from the top of the list, but has relatively few mysteries (the current being CSI, which is part "cool new stuff") and very few period dramas.

Some popular TV/movie genres are conglomerations of other genres; Cop shows, for example, are often dramas (primal) with some deductive and experience elements. Some genres don't seem to translate to virtual worlds at all; News broadcasts are informative, which removes the interactivity from virtual worlds. Changing the information into experience might work though.

Of course, most virtual worlds can be categorised into a few different genres, and include minor elements of all genres. Everquest II is primal, reptilian, cool new stuff, and inductive. Second Life emphases primal and creation.

Genres or player types?

To demonstrate why genre (using this definition) is important, let me give an example. Lets say you introduce a new feature: Sailing ships and the ability for one player to be captain while other players act as crew.

You need to ask yourself, "Why does the player wish to be captain?" and "How does that motivation affect the implementation of the captain sub-game?"

Described this way, my genres look an awful lot like player types, although they differ from Richard Bartle's killers, socialisers, explorers, and achievers, or Nick Yee's facets. (In this document, I actually lump killers, socialisers, and some achievers into the "primal" category.)

If you try to create a world that caters to all genres, then my genres ultimately turn into player types. But, to create a world that caters to all genres (player types), you have to do one of the following:

Of course, there is one other option:

All four of these solutions are viable, but they all have tradeoffs. If the restaurant business is any guide...

The other problem...

Virtual worlds are not entirely like restaurants; restaurants (almost) never get in the situation where a dish is in limited supply and only a small percentage of the customers that want to order it are allowed to get it.

Inevitably, if a world has 100 players, 20 of them will wish to captain their own ship, but there will only be 5 slots. (You could create more captain slots, but then the world wouldn't have enough players to be crew.)

How does the world decide who gets to be captain? (Copied from The parlour, the lobby, and the sand box.)

Designers have the same problems here as with genre coverage. A world can allow "all of the above" as ways to be captain, or it can specialise in certain types of players.

Does that mean that the criteria used to determine which players get power ends up producing another dimension to the genre?

Conclusion

It seems like genre can't be defined by codebase, or what player types a world caters to, or how power is distributed. There are too many degrees of freedom:

I suspect that "genre" will fall out of the choices as more worlds become available. Single-player games and MMORPGs already have some associations forming. For example:

Undoubtedly, more genres and sub-genres will follow.

 

 


 

Game classification

(Back to TOC)

17 June 2005

by Mike Rozak

Below are some dimensions that can be used to classify games:

Theme:

Available sub-games and activities:

Characters:

Miscellaneous:

 

 


 

We don't always get what we want

(Back to TOC)

17 June 2005

by Mike Rozak

In his books, Chris Crawford points out that to design a game, you must ask the question, "What will the players do?" Although it's a simple question, many games don't seem to have a decent answer to this question.

When I think about the question, "What will the players do?", some related questions come to my mind:

I have discussed the idea of "What players want to do" in some previous articles, such as in Virtual world equation, where I provide several examples of desires/goals that players wish to fulfil. However, I assumed that players would eventually be able to fulfil those desires, as they imagined them. This is not necessarily the case...

To illustrate my points, I'll draw on two example desires/goals that a player might have:

Working towards a desire/goal

When I was young (around 10 years old) and was GM-ing my first Dungeons & Dragons campaign, I quickly realised that my players wished to be powerful within the world, and able to defeat any monster they came upon. Not wishing to disappoint them, I handed out some fabulous magic items that made their characters virtually invincible... As you have undoubtedly guessed, it ruined the game.

Likewise, if a mountain climber were instantly teleported to the summit of Mt. Everest, they would be very unhappy. However, someone wishing to run an inn might not mind being given an already-working inn to run, because the fun part for them is the destination.

When designing the path to achieving a desire/goal, the designer might want to consider the following:

Experiencing the desire/goal

What happens when the player achieves his desire/goal? What is the summit of Mt. Everest like? What does an innkeeper really do for his job?

After the desire/goal has been achieved

At some point, all mountain climbers (except the dead ones) must leave the summit, and an innkeeper must give up his inn. What happens then?

Complexities

Throughout this document I have discussed a player's desires and goals in somewhat simplistic terms:

What does this mean?

 


 

The iron law of reality

(Back to TOC)

20 July 2005

by Mike Rozak

When designing a virtual world, the topic of "story" vs. "game", or narratologist vs. ludologist, continually surfaces. It is an inescapable problem: People want to interact with a story (which includes intelligent NPCs, a world designed to maximise the telling of the story, and coincidences galore), while they don't want to accept the consequences of a story (lack of free will).

The more I ponder the problem, the more I conclude that it's endemic to reality, not just virtual worlds.

I already wrote up some thoughts in Story and plot vs. freedom in virtual reality. Here's a quick summary:

1.         In a world (a collection of physics, geography, etc.), there are practically an infinite number of outcomes for "what happens". Star Trek likes to call these outcomes "alternate realities".

2.         Most of these outcomes (alternate realities) are uninteresting to the reader/player of the world. What makes an outcome (un)interesting is reader/player specific, but I investigate the idea in an Evolutionary explanation for entertainment, along with many other writeups in DRT.

3.         A story is a narrative chosen from one of the more interesting outcomes (alternate realities) that can be produced by a world. Since the author of a story is also the inventor of the world's physics, geography, history, etc., he has a pretty good idea what outcomes are possible and which lead in the most interesting directions.

4.         A game leaves all the choices up to the player. Unfortunately, a player doesn't know much about the world. He might know the physics, but he doesn't know too much about the world's geography and characters, or where the character are and what they're doing at any point in time. The result is a series of choices that usually result in uninteresting consequences, since (after all) most uninformed choices lead to uninteresting outcomes.

5.         What the player really wants is a world with choice, where every choice leads to an interesting outcome; which is a combination of a story and a game.

Unfortunately, a mixture of story and game is like mixing oil and water.

1.         If a player is expected to make choices that usually result in interesting outcomes, the player must know a fair amount the world he is in.

2.         The more the player knows (or the more intelligent the player), the better the player is capable of determining if a choice is a good choice (or a bad one) based on a "how interesting is the outcome" metric.

3.         If the player can clearly identify a few best choices, weeding out lots of really undesirable ones, the player is suddenly faced with fewer choices. As I pointed out in, Choice, a choice that is obviously bad isn't really a choice, since no one will chose it.

4.         Therefore, a player who increases his intelligence/knowledge in order to be able to make choices that result in interesting outcomes ultimately finds that he has no/little choice.

5.         An experience without (much) choice turns into a narration. Frank Herbert pointed out this dilemma in the Dune series, where the prescient Mua 'Dib was left without choices.

6.         Narration does not work well as a game because (a) games are supposed to be about choice, and (b) television/movies are inherently better at narration than games.

NOTE: A good narrative has interesting outcomes that are not 100% predictable. A good author never gives readers exactly what they expect, perhaps an "imaginary number" version of choice.

7.         Thus, players that learn enough about the world to accurately predict its outcome (and reliably produce interesting outcomes) don't enjoy the world because they have no choices. Players that cannot reliably predict the outcome don't enjoy it either because most choices will result in uninteresting outcomes.

8.         There is a fine line between a predictable world with interesting outcomes but little choice, and an unpredictable one with lots of choice but uninteresting outcomes. Raph Koster's book, "A Theory of Fun", comes to a similar conclusion, that gameplay is about understanding the pattern, getting bored, and moving onto new patterns.

The introduction of God

All is not lost though; it is possible to mix oil and water, and story and game. Unfortunately, the methods are not always palatable.

Table-top RPGs successfully mix story and game; they do this by designating one player as a game master (GM). The purpose of the GM is to create the world, play the NPCs, and adjudicate the rules. Because the GM has so much knowledge about the world (which is mostly made up on the fly anyway) and is friends with the players, the GM can tailor the experience for the players. Basically, the GM tries to make the outcomes of the player's choices as interesting as possible.

Most of the time, the GM's subtle manipulation of reality is gladly accepted by the players because they realise it's producing a more interesting experience. Once in awhile, the GM's maneuverings annoy the players to the point where some storm out of the room. They (usually) come back though because the players are also bonded to the GM by friendship. They trust that his decisions are in their best interest.

GM's don't work terribly well in virtual worlds because (a) they cost too much to hire one GM per six players, (b) 10,000 GMs running around the world with 60,000 players is bound to result in conflicts between the GMs, and (c) players will (in)correctly blame their or another GM of favouritism.

Theoretically, a very intelligent AI could be written that would solve all of these problems. Unfortunately:

1.         If the AI were more intelligent than the players and/or knew more about the world (including all the world's players), it could subtly manipulate the world to maximise the players' fun.

2.         To know what was fun for each player, the AI would need a psychological profile of each player. It would have to know how to produce in-world events that would be interesting for the profile.

3.         A successful AI would create a world in which players had full freedom of choice, and which most choices would result in interesting outcomes (to the player). The AI would also be able to juggle the conflicting interests of the 60,000 players in the world.

4.         Unfortunately, many/most people don't like being manipulated. If they think they are being controlled, they either rebel or find ways to control their controller.

5.         Additionally, while players don't claim favouritism from stupid (contemporary) AIs, one intelligent enough to run a world could be attributed with favouritism.

6.         The solution is to hide the AI so that the player's don't have proof that it exists; make the AI work "in mysterious ways". How the AI's development team gets hidden is "an exercise left up to the reader"... which means that I have no clue how it would be done.

If it existed in the real world, the intelligence that I just described would be called God. The game AI's purpose is to make virtual life fun for the players. According to modern religions, the purpose of the real-life God(s) is usually anything but acting as the people's entertainer.

More unknowns than knowns

While discussing GMs, I glossed over another trick that can be used to mix story and game. If the world is filled with more unknowns than knowns, the GM (or AI) can always pull a deus-ex-machina and claim that events just happened by chance.

It happens all the the time in the real world; you're on holidays in the Grand Canyon and just happen to run into an old friend. A penny happens to by lying on the ground, presumably fallen out of someone's pocket.

Storytellers ostensibly despise this device, although they still use it. They do disguise it though, by describing why a character's friend was also in the Grand Canyon, or who dropped the penny just 10 minutes before. It amounts to the same thing.

One technique for introducing unknowns into a world is to fill the world with armies of NPCs whose job it is to drop pennies or cause "coincidences".

Fractured reality

If five people witness a car crash, police will hear five (or more) different descriptions about what happened. You could attribute this to the human brain's inability to remember details when it wasn't paying attention (which is the likely cause), or you could claim that all five people were accurately describing what they saw; they just saw different and conflicting events.

With 60,000 players running around a virtual world, at least one thousand of them will want to be Sir Lancelot (or Napoleon). Unfortunately, reality dictates that only one can be Sir Lancelot and one can be Napoleon.

The solution?

Fractured reality; Allow players to be Sir Lancelot (or Napoleon) in their own eyes, turning them into Don Quixotes. So long has their delusion is not crushed by the chance meeting of another Sir Lancelot, they'll be fine.

On a more subtle scale, players do not have to see/perceive exactly the same things. A dropped penny might only be visible to the player that is supposed to pick it up.

Fractured reality explains where all the pens disappear too, and why you always find your glasses on the table that you just thoroughly searched two minutes ago.

Ultimately, what does this mean?

I'm not sure, but here are your choices...

 

 


 

The ecology of a MMORPG

(Back to TOC)

20 July 2005

by Mike Rozak

Once again, I've spent my time trying to understand how/why MMORPGs (and MUDs) work. The thoughts contained herein are a conglomeration of elements from Richard Bartle's Designing Virtual Worlds, some of my writeups (such as The player pyramid and Intertwined relationships), as well as my wanderings through MMORPG message boards.

What I noticed while exploring MMORPG message boards is that (to no-one's surprise) there are several different "factions" of players, with each faction requesting a different set of features:

Other feature factions are also present, either as faint murmerings or theoretical possibilities:

The ecology

What's so important about feature factions?

They can be used to explain why a MMORPG/MUD exists and is stable...

If I begin with a CRPG that's tailored for single-players, add multiplayer ability, and extend the length of gameplay, I attract the following feature factions:

Some things to notice about this ecology:

Other ecologies

Some feature factions are left out of the traditional MMORPG.

Ecology == Genre

In case you haven't noticed, the different ecologies correspond to "genres". The key (according to this theory) to a successful world design is to develop a successful player ecology.

 

 


 

Of mice and elephants

(Back to TOC)

20 July 2005

by Mike Rozak

I am a single person trying to create a virtual world in a real world where virtual worlds have budgets of $20M and rising. ($20M = about 200 man years). I am a mouse scampering amongst the titanic feet of elephants.

Consequently, I spend a large amount of time figuring out where the elephants will be heading, to help ensure I don't get trodden on.

I have written some of my thoughts about the subject in Small VW operators vs. large, and Text vs. graphics. I have some more thoughts, or at least some realignment of thoughts, that I thought I'd share...

In the last six months I have played both World of Warcraft and Everquest II. Both of them are very impressive, with copious content and stunning eye candy. There is no way I can compete on that playing field. I could spend three years developing quests and graphics equivalent to WoW and EQII, and not produce 1/10th of what WoW and EQII contain. The prognosis gets even worse since the bleeding edge is a moving target. Both these games were conceived and budgeted over three years ago. The mass-market worlds begun today will have even larger budgets (probably double, at $40M), and will be even more titanic.

Therefore, I must admit to myself the following facts:

I could try to create an army of volunteers to create an open-source WoW or EQII, but then I wouldn't be a mouse any more. Herding such an army would be a feat akin to herding cats, something I'm not terribly excited about doing. People more politically skilled than I can do that.

So what does this mean about any world I (or other mice) try to create? And what are the implications?

1.         Content will be limited, perhaps to 50 hours.

At 50 hours of content, the entire game changes. The experience will be more like a single player game that you can play with friends or use to meet people. It will have a beginning, middle, and end, and many of the sub-games that MMORPGs rely upon, such as guilds and trading, won't exist. Players will regularly jump from one world to the next. See my Anti-MMORPG writeup.

My realisation that I will have less content than a mass-market MMORPG is interesting; I often think of my role as the author of a novel, while a mass-market MMORPG is a movie. Since movies are shorter than novels, I would expect niche-market worlds to take longer to play than mass-market worlds. The opposite appears to be the case. Coincidentally, graphical worlds become more cost effective as they get larger because art assets (and actor's voices) can be reused.

2.         Eye candy will be limited. Graphics will still be needed, or the world won't attract any paying players. The eye candy won't be stellar though.

I need to replace the lost graphics with something, and that something is text. However, text and graphics don't mix so I'll need to use speech. Since recorded speech is too expensive, I must use synthesised speech.

3.         Mice eat the food that's too small for the elephants to see, or which is impossible for elephants to get to. A mouse can eat the nutritious grass seeds while elephants must consume the entire grass, which is mostly indigestible fibre. As a mouse, I will have to target niche-market audiences that elephants can't/won't reach.

4.         It's cheaper to maintain a virtual world than to build a new one from scratch. Does this mean that small virtual worlds authors will create one world, and continually evolve it for the rest of their lives? This sounds boring to me, although it's precisely what Tolkien did. I'd rather spend a year creating a world, then move onto another one, and then another. Unfortunately, such a course would leave me with several small worlds, all but one being "dead"; a dead world might still have players, but it wouldn't be updated nor would it be monitored much.

This is a dilemma for me: A small corporation could stick to one world since as employees get bored with the world they move to a different corporation. A single author doesn't have the option to move, and since they'd get bored, I suspect single authors will abandon the old world to create a new one.

The implications have implications:

1.         A 50-hour experience means:

1.         The experience will have a beginning, middle, and end, and probably incorporate story similar to the way an adventure game or CRPG does.

2.         The player's role within the world will be specialised; players can be "forced" into the role of detective or coal miner, and the experience can revolve around being a detective or a coal miner. The traditional MMORPG tank, spell caster, and healer will be rare.

3.         Some plots simply cannot be maintained for a long period of time, but might be possible in shorter form. You wouldn't want to spend 500 hours experiencing what it's like to live in a concentration camp, but 5 hours of the horrendous experience might be palatable and prove to be very educational.

4.         Players will be able to play with friends, or team up with players online. Many players will partake in these virtual worlds for the sole purpose of meeting people (that they can then meet in the real world), using them as a form of dating service. PvP, guilds, and other MMORPG social institutions are unlikely.

5.         For every elephant that roaming the savannas, there are thousands of mice. Likewise, there will be many more 50-hour worlds than large worlds.

By the way, this has a sub-implication about procedural content... Smaller worlds will reply on procedural graphics to cut costs, but content will be hand crafted. Procedural content is most useful in everlasting experiences like Diablo.

6.         Shorter worlds will target gamers who only play five (or fewer) hours a week.

2.         A verbal world can produce some forms of content that a mostly-visual world cannot:

1.         Narration works much better verbally than visually; "Orsin thoughtfully caressed the rim of his wine glass with his finger before taking a small sip to taste, and then quaffing the lot." You can try to animate this sentence, but it will be very expensive (particularly when multiplied by the thousands of NPCs, each with their own animation) and won't ever convey the same meaning.

2.         Similarly, role players can use narration to enhance their role play.

3.         Non-Euclidean space and non-contiguous time are much easier to handle verbally.

4.         A verbal description can gloss over unimportant information; "The princess lived in a white tower with a single window at the top." What architectural style was the tower built in? What material? How big was the window? Etc. Someone creating an image for the tower must fill in all this information even though it isn't important for understanding the experience.

5.         A narrative world lends itself to text (or speech recognition) commands from the player. Verbal commands can convey more complex actions than point-and-click.

3.         Niche markets include:

1.         More intellectual.

2.         People who speak languages other than the top-6 languages. Similarly, a niche world could target a Native American myths rather than relying on a European or Asian mythology.

3.         More experimental.

4.         More extreme - Large worlds can't be offensive or contain any ideas other than the bland; Small worlds can have opinions.

5.         Special interest groups - A virtual world based on Mycenaean trading, for example.

4.         "Dead" worlds - I'm not certain about the implications of dead worlds. Maybe I'll write something up later.

 

 


 

Problem solving

(Back to TOC)

19 August 2005

by Mike Rozak

Hunting and gathering

Chris Crawford says that the first thing to decide when designing a game is "what players do".

To my eyes, both The World of Warcraft and Everquest II are the same game because players "do" the same thing. If you tear away the trappings of both games, and attempt to distil them to their essence, WoW and EQII are about "hunting and gathering". Players spend their time extracting resources (XP and loot) from the world and then use the resources to level up their character so they can then extract further resources. (This line of thought isn't at all new, by the way.)

Players quickly learn that hunting and gathering are made safer and more efficient by forming hunting parties (called parties or groups). They also learn that forming tribes (called guilds) leads to safety in numbers, a source of people to form hunting parties with, and a venue for sharing wealth and knowledge. As Raph Koster has pointed out, these tribes usually consist of 100-150 members, a social limit that seems to be hard-wired into the human brain.

The monotony of repetitively hunting and gathering the same objects is mitigated by quests, which provide a purpose for the hunt. A player's quest list also provides an unending array of satisfying milestones. If the completion of one quest doesn't produce a follow-on quest, the player still has a large collection of quests waiting to be finished. From my own play experience, quests are key; if quests weren't in the games, both would be incredibly dull.

In my opinion, The World of Warcraft creates a hunter/gatherer lifestyle more successfully than Everquest II. In WoW, players begin in a small village and complete quests that protect and aid their village. They are then encouraged to visit their racial metropolis, where they continue to protect and aid their city... which isn't exactly hunter/gatherer any more, but still has a tribal feel. Once part of the larger world, players learn their city is part of a confederation of four aligned nations, which they must protect and aid. Part of this duty includes raids on the enemy players' villages and cities. EQII wraps quests in a much more capitalistic and modern guise, with most of the quests ostensibly benefiting individuals in the city, not the community. At its initial release, EQII didn't have player-vs.-player raids either. WoW has sold more than four times as many copies as EQII. Does WoW's admission of its hunter/gatherer foundation have anything to do with its higher sales?

WoW and EQII are not alone in the hunter/gatherer foundation; Most MMORPGs (as well as most text MUDs) follow the same formula. Theoretically, a single-player hunting-and-gathering game could be written, but it would lose the important aspect of community that a multiplayer game allows. Having other players to interact with to the illusion of a hunter/gatherer lifestyle.

Defeating the boss monster

What I find fascinating about hunter/gatherer MMORPGs is that they evolved from text MUDs, which evolved from single-player adventure games, which came from pen and paper RPGs. Pen and paper RPGs have almost no hunting-and-gathering component.

To illustrate how different MMORPGs are from RPGs and other computer games, let me provide an example...

Imagine that a player of a MMORPG comes across a boss monster that's too tough to kill. What does the player do to get around the problem?

1.         The player could keep attacking the monster and rely upon luck to get through. After all, if the player's character dies, it will be resurrected. Even if it requires a hundred deaths, the virtual dice will eventually roll in the player's favour, and he will overcome his foe. MMORPGs prevent this "try and try again" solution by introducing a death penalty.

2.         The player could do some planning and come up with a strategy for defeating the monster. MMORPG strategies plateau at "pulling monsters"; more complex strategies don't provide much of an advantage. MMORPG designers intentionally design out strategic solutions, for a few reasons: (a) Any winning strategy against a boss monster will be published on the Internet and will be known by every player within days, eliminating the challenge of the boss monster and unbalancing other aspects of gameplay. (WoW recently had such a problem with one dungeon and declared the strategic approach "illegal".) (b) Most players will forgo strategy and rely on options (3) and (4) anyway.

3.         Any boss monster can be defeated by a sufficiently large group of players. All the stumped player has to do is (a) team up with other players who are stuck at same the boss monster, or (b) call in some friends. MMORPGs are, after all, multiplayer games. (Of course, the more players involved, the less loot each one gets. But since monsters re-spawn every minute, the nicer players wait around so everyone gets a shot at the loot.)

4.         Because there is an infinite supply of non-boss monsters to produce infinite experience that can be spent to increase the character's power, a stymied player merely leaves the unbeatable boss monster and returns after few days when his character has levelled up. Players won't even bother to come back unless the boss monster yields up a spectacular treasure, or is blocking another part of the world that, in turn, yields spectacular treasure, or some new scenery to break the monotony of hunting and gathering.

Compare the MMORPG solution to the approach players take in other types of games:

Problem solving

I claim that most MMORPGs are about hunting and gathering. Conversely, pen and paper RPGs are about problem solving. (Some role players might disagree; if so, just pretend RPGs are about problem solving, for the sake of the argument.) When RPGs are distilled to their essence, the players enter a room and are faced with a challenge. They must come up with a proposed solution to the challenge and act upon it. The challenge often includes combat, but can also take the form of traps, puzzles, or NPCs that must be placated. Magic items and spells usually provide new ways to overcome the challenge (such as invisibility or flight) while magic items and spells in MMORPGs just result in more damage capability. In all cases, a party that spends ten minutes producing a strategy or plan is more likely to succeed than one that just rushes in.

Hunting and gathering (MMORPG-style) doesn't even vaguely resemble problem solving. If pen and paper RPGs are about problem solving, how did their MMORPG descendants turn into games about hunting and gathering?

The transformation occurred when RPGs were translated from pen and paper (and the minds of the GM and players) into a computer simulation. Human brains can imagine a much more complex and varied "world physics" than can presently be programmed into a computer. When the pen and paper RPG party encounters a boss monster they can charge in and attack it, as can a party of players in a MMORPG. The RPG party could instead send someone to climb into the rafters and cut the rope that holds the 16 ton weight suspended over the boss-monster's head; a computer can enable this solution, but only with a fair amount of programming and 3D modelling. Alternatively, the RPG players could invent a solution the GM had never imagined, like going to the store and buying superglue, which they then splash all over the boss monster. The enraged and perplexed monster then chases the party through a narrow crevice where the monster gets glued to the crevice walls, giving players enough time to take his loot; computers can't handle such creative solutions.

When a game whose basis is problem solving is translated to the computer, the problem solving must somehow be simplified:

Back to hunting and gathering

The "problem" to solve in a MMORPG is how to deal with other players, how to cooperate with them, how to get them to do what you need them to do, and how to defeat them. The world is only a setting for these problems to arise (with some encouragement from the world design) and then be resolved by the players.

The hunting-and-gathering foundation works because:

1.         Hunting and gathering both encourage players to work together. Players must solve the problems of how to find and join a similarly-minded group. They must resolve personality issues, resolve real-world schedule constraints, determine one's functional role within the group, etc.

2.         Hunting and gathering are based on limited resources, causing conflict amongst the players over who harvests the resource. Players must solve the problem of limited resources amongst themselves, through combat, negotiation, or whatever other means they come up with. These issues must be solved for intra-group and inter-group conflicts, where the group might be a party or a tribe.

3.         The formation of small groups (aka: parties) and tribes (aka: guilds) produces social structures that, in turn, present problems to members of the group/tribe. Players in a group contents leadership positions and/or convincing the group to follow a specific agenda.

 

 


 

Exceptional physics

(Back to TOC)

20 August 2005

by Mike Rozak

Two abstractions of "physics" are used in games:

Note: Some designers (like Chris Crawford) use the term "verbs" and "nouns". "Attacking" and "digging" are the verbs, and all the objects that can be attacked or dug are the nouns. In universal physics, few verbs exist, but they work properly with every noun. In exceptional physics, lots of verbs exist, but each verb only works with select nouns.

Each abstraction of physics has advantages and disadvantages...

Of course, games are a mixture of universal and exceptional physics. MMORPGs, which mostly rely on universal physics, use exceptional physics for conversations with NPCs. Adventure games, which are based on exceptional physics, use universal physics for movement.

Single-player CRPGs vs. multi-player MMORPGs

Producing a matrix of games that emphasise universal vs. exceptional physics, and single vs. multiple player clarifies some of the common game categories:

Changing a single-player universal-physics game (aka: CRPG) into a multi-player game (aka: MMORPG) has an interesting effect on the game, as I discussed in Problem solving. Here's a brief synopsis:

1.         For a problem/challenge to be interesting to the player, it must be carefully balanced so that it isn't too easy nor too difficult. Because the player's own skill needs to affect his rate of success, this usually means the player must apply some thought/strategy to the problem to increase his chance of success. (In the case of FPS, dexterity is also required.)

2.         In a game with persistent characters, the designers must ensure that when the player's character reaches a problem/challenge, the character's abilities and power will be well matched against the problem, whether it's a monster, trap, or lock to be picked. If the character is too weak, the player will never be able to solve the problem, no matter how much strategy is used. If the character is too strong, strategy is unnecessary, there's no challenge, and the game is no longer fun.

CRPGs deal with the balance issue by ensuring that experience points (which increase a character's abilities and skills) are doled out such that the character's level will be appropriate for the challenge when he reaches it. This demands a finite supply of experience points.

MMORPGs must continually respawn monsters so that every player will get his chance at a kill, which means there is an infinite supply of monsters, and consequently, and infinite supply of experience points. Hence, MMORPGs can't guarantee that a character's level will be appropriate to the challenge.

Work-arounds exist: MMORPGs can offer rewards appropriate to the character's level. A more powerful character won't find the lower-level rewards beneficial, so he won't (theoretically) undertake the challenge. In reality, many players find it much easier to attack weaker monsters (and get fewer rewards more easily) than to challenge level-appropriate monsters.

3.         In a game with multiple players, scenario balance is impossible. If a single player can't get past a challenge, he just waits until more players reach the challenge, and they all tackle it together. Alternatively, the player can call in his friends when needed. Of course, the treasure will have to be divided amongst all the players. This isn't a problem since MMORPG monsters respawn every minute, and the players can just re-kill the same monster until everyone has the desired loot.

Work arounds exist: MMORPGs with instanced dungeons don't respawn the boss monster, who happens to have the best rewards. Players that wish to acquire the boss monster's loot multiple times must work through the entire instance each time.

4.         Even if all these problems are solved, multiplayer games often become competitive. Competitive players (a significant portion of the MMORPG population) will read walkthroughs to make their life easier. Any problem that has a fixed solution/strategy will appear on the walkthroughs, eliminating the challenge for competitive players and unbalancing the game.

5.         Consequently, a multiplayer universal-physics game cannot produce challenging scenarios for players. The challenge of a MMORPG is not the game; The challenge comes from dealing with the other players in the game.

Single-player vs. multi-player adventure games

What happens when a single-player adventure game is turned into a multiplayer adventure game? The problems that appear when a single-player universal-physics game is turned into a multiplayer game might also appear; The game's challenge might simply disappear, and fun along with it.

Two basic types of challenges present themselves in exceptional-physics worlds:

A multiplayer exceptional-physics game must do the following:

 

 


 

The dating game

(Back to TOC)

20 August 2005

by Mike Rozak

Why people play MMORPGs

Yes, it's back to that old question... Why do people play MMORPGs? Or, more precisely, why do they play a massively multiplayer game when they could be playing the single-player version for (typically) less cost and (typically) a better game.

Awhile ago I would have said the reasons for playing MMORPGs vs. their single player equivalents (usually CRPGs) is the other players, and it all comes down to socialisation. However, it's not that simple. Let me list the reasons why (I think) a player might wish to play a MMORPG (instead of a CRPG). The list isn't exhaustive; Feel free to add/remove items as you wish.

Meeting new people

MMORPGs do a good job of catering to most of the above-listed reasons that people play. For example: MMORPGs go out of their way to encourage players to join guilds by providing built-in guild features such as: Guild E-mail, guild chat, guild management, guild web-pages, raid content designed for guilds, and clearly displaying which guild players belong to.

MMORPGs do not do a good job of helping people make new friends. Sure, MMORPGs encourage players to form parties and explore dungeons together. However, those dungeon crawls are 99% action with very little time for players to sit down and chat. MMORPGs are also missing some key features for meeting people, many of which were common in text MUDs.

The following features allow players to get an impression about other players from a distance:

Other features are useful after a conversation has begun.

Niche communities

MMORPGs don't provide many features for meeting new people because most MMORPG players don't want to meet new people. They want to play with people, but not actually meet them. They play with their friends, and if they group with strangers, all they care about is what class the stranger's character is. Half the time I've been invited to join a group, the party leader hasn't even chatted to me first; they just click "Invite into group" without a word.

In my mind, MMORPGs are large virtual worlds with tens of thousands to millions of players. Text MUDs are small social worlds with hundreds to thousands of players. Wandering up to someone in a MMORPG and striking up a conversation is like wandering up to someone in an airport and introducing yourself. The chances are, you won't have anything in common with the other person other than you're both flying to New York City. MMORPGs attract a wide variety of players, most of whom will have different interests than you... other than the fact they like to play MMORPGs. They probably won't like the same music, be the same age, have the same life philosophies, or have the same goals in life.

Most people don't try to meet new friends through random encounters in airports, and they don't try to meet new friends in MMORPGs. To meet friends in real life, people go to parties (organized by their own friends) or join community groups (like hiking or religious groups). The same applies to virtual worlds.

People who wish to meet other people will visit virtual worlds that target niche communities. At the moment, text-MUDs provide a better way of finding a niche community than MMORPGs. For example: You can find text-MUDs dedicated to:

A niche community is one with a very small market, around 1% of the population or less. I suspect almost everyone fits into at least one niche community. Some people like hard core science fiction, others are into souped-up cars, while others like to make pottery. The list of niche communities is enormous; you can get an idea of how huge by browsing your local book store or magazine retailer. Hundreds of categories exist, some of which translate into virtual worlds:

Each of these groups attract certain personality types, and people with very specific interests. If you're a rev-head and walking around an auto show, the chances are pretty good that anyone you talk to will also be interested in cars. Talking to someone at random an auto show is much likely to result in a friendship, or at the very least a good conversation, than meeting a stranger at the airport. The same goes for a virtual world targeted at a niche group.

The dating game

What conclusions do I draw from all of these thoughts so far?

1.         There are people who want to meet new friends... This is a bit of an understatement. Most of the world's population fits into this category.

2.         Existing MMORPGs do not provide the features and design needed to meet new people, and the cannot due to their all-inclusive nature.

3.         Virtual worlds targeted at niche communities are an excellent way to meet new friends.

Niche virtual worlds that are intended for people to meet friends must be designed differently than traditional MMORPGs:

1.         The purpose of the world is for people to meet one another. Players who want to be the alpha (fe)male can go to a traditional MMORPG that emphasises levels and PvP. The same goes for players who wish to be part of a guild, who want unlimited content, etc. Most players will play in both mass-market and niche virtual worlds, just like many people watch mass-market television shows and still subscribe to niche-market magazines.

2.         The world must provide the features that I listed in "Meeting new people".

3.         The purpose of the world's content is to attract people of a specific niche all into one place so that they can meet one another.

1.         It must be considered fun and entertaining by members of the niche.

2.         It must be considered un-fun and boring by people who don't fit in the niche.

4.         Once in the world, the content should encourage and help players to meet up inside and outside the game.

5.         Each niche community will be supported by a handful of worlds, just like a few magazines exist for every niche community. Worlds will go in and out of fashion, just like everything else.

 

 


 

Choice and consequences

(Back to TOC)

13 September 2005

by Mike Rozak

You can simplify the structure of a MMORPG, adventure game, CRPG, as well as many other computer games, into the following recipe:

1.         The player is presented with a menu of quests they can undertake.

2.         The player choses one and completes whatever actions are needed to complete the quest.

3.         As a consequence of completing the quest, the world or player character changes.

4.         Repeat. First, remove the completed quest from the list and perhaps append a few new quests.

Of course, I have stated the obvious here, but sometimes obvious statements identify a pattern.

Menu of quests

Notice how MMORPGs, adventure games, CRPGs, and other first-person reality games offer a menu of quests, not just one, and not thousands of choices. MMORPGs tend to have 10-20 quests available to a player at any time. Adventure games always have 3-5 puzzles available. CRPGs likewise provide a selection.

The fact that a menu is offered is significant. Let me illustrate why menus of quests are available for an adventure game:

1.         Adventure games typically have 3-5 puzzles available in every region of the game. Once those puzzles have been completed, a door is unlocked and the player is then given access to the next region, again, with 3-5 puzzles. A typical adventure might have 5-10 regions, producing 25-50 puzzles (aka: quests) for the entire game.

2.         Adventure game designers have discovered that if only one or two puzzles are provided per region, problems arise:

1.         If a player gets stuck on one puzzle they can't "put it down" and come back to the puzzle later with a fresh mind. In a game with only one or two puzzles per region, players inevitably get stuck, put the game down, and never come back. With more puzzles in the queue, they're more likely to keep playing.

2.         When players solve a puzzle, open a gateway, solve another puzzle, and then open yet another gateway, they feel like they're been guided through the experience and have no choice. They rebel, and stop playing.

3.         One would think that if 3-5 puzzles per region are good, more are better. Adventure games don't provide a large menu of puzzles (quests) though:

1.         A well-known rule of user interface design it to limit the number of choices presented to a user. Too many choices overwhelm the user. The same goes for games.

2.         An adventure game is composed of 25-50 puzzles. If the game designer places 15 puzzles in each region, then the adventure game consists of only two or three regions. Since the introduction of new regions is used reward the adventure-game player with new scenery, and to indicate progress, fewer regions results in a less-fun game.

3.         If an adventure game were to present all its puzzles in one region, the resulting experience transforms into the freely-available experience from game websites like Yahoo Games. The player is merely presented with a buffet of choices. (See Stop the buffet.)

The same rules and reasoning applies to MMORPGs and CRPGs. If too few quests (or activities) are available the player feels railroaded. Too many quests (and activities) is overwhelming and less fun.

Undertaking and completing the quests

I have written about quest design elsewhere, and won't go into detail here. See The toy room as well as many other articles on Deeply random thoughts.

Consequences

Once the player completes a quest, the world or PC changes. Three types of change can occur:

Tied up with the consequences is the ability for the player to chose what form the consequences will take. For example:

The types of consequences vary depending upon the game genre:

Several types of choices

Notice that adventure games, MMORPGs, and CRPGs all provide the following choices:

1.         Players have a choice of which quest they wish to tackle at the moment. The quests to chose from are limited.

2.         Players have a choice in how the quest will be completed/solved. Each quest can only be solved in only a limited number of ways though, because programming in a large number of alternate solutions is expensive. See Problem solving.

3.         Players have a choice (often implicit) in how the completed quest will affect their characters, the world, or other players in the world. The number of outcomes is also limited, and related to the quest; killing the evil overlord won't result in a cure for in-game cancer.

A game which offers too few or too many choices (in any category) isn't as fun.

Categorising some popular genres

Verbiage

I just described a system where players are provided a menu of quests, chose one to work on and solve using one of a variety of approaches, and then upon completion of the quest, change the world.

I can restate the cycle in much clearer terms:

1.         The player is provided a menu of goals (or sub-goals) they could undertake. If completed each goal results in changes to the player's character, other player characters, or the world.

2.         To complete the goal, the player must determine a solution (out of several possible) and act on it. Adventure games tend to require more effort in coming up with the solution than acting on the solution. CRPGs don't require much time to problem solve, but more time to act. See problem solving.

3.         Once the goal is achieved, the goal's effects are applied to the PCs and world. See We don't always get what we want.

4.         Repeat. Ideally, completing one goal will lead to other goals so that the player is never without goals.

Consequences of stating the obvious

So, after stating the obvious, have I learned anything?

1.         Both WoW and EQII are pushing the upper bounds for how many quests they offer a player at any point in time. Hard-core players like the freedom of 20+ quests in their journal, but more casual players will be overwhelmed by the choices.

2.         Both WoW and EQII provide players with a choice of reward items when they complete a quest. WoW displays the reward items before players undertake the quest, while EQII only displays the choice after the quest has been completed.

If the ability for players to chose the outcome of their actions or to chose their actions based on expected outcomes is important (which I claim), then showing the rewards ahead of time is a better game-mechanic than keeping the outcomes hidden.

3.         A skill-based system allows players to control the outcome of a quest (good); pure level-based systems do not (bad). However, providing players too many skills to choose from overwhelms them. (Most MMORPGs have a hybrid level system combined with skills.)

4.         A typical MMORPG doesn't provide enough different ways for players to complete a quest. Players can either attack the monster with a sword, or attack it with an axe; there's no way to trick the monster, etc. Nothing new here.

5.         A typical MMORPG doesn't allow players to change the world! The ability to change the world is vitally important. If a player cannot change the world, and they cannot engage in PvP (and change other players), players spend all their time changing the only thing they can change, their character's level and equipment.

A way for players to change the world is desperately needed. See Fractured realities.

 

 


 

Fractured reality

(Back to TOC)

13 September 2005

by Mike Rozak

I've mentioned fractured reality before, but I thought I'd put all my current thoughts in one place...

The problem with a monolithic reality

In the real world, we assume (rightly or wrongly) that there is one reality that we all live in. If our individual perceptions of reality are different, it's because we are misperceiving reality, not that reality is different for each of us.

A monolithic reality doesn't work well for virtual worlds (in my opinion). The problem was noticed from the very beginning of text MUDs, since in a monolithic reality, if one player changes the world, it remains changed for all players. This means that if a player kills the the evil overlord, then for all players thereafter the evil overlord is dead, which is a bit of a bummer for all the other players who wanted their chance at defeating the evil overlord.

Some workable solutions exist:

Fractures in reality

Some solutions exist that "fracture" reality, creating a different reality for every player.

Why fractures are important

The reason why fractures are important is that players want to be able to change the world. Even if they don't control exactly how the world changes, they still want their actions to have an effect. (See Choice and consequences.)

In all of the monolithic realities, except the content-free one, players have no real effect on the world. The next time they log on, the world is exactly the same as before. Players can and do turn a blind eye to this, but ultimately the inability to change the world diminishes the experience.

I am not a big of content-free worlds either. Players can change the world in the content-free monolithic-reality, but only in small ways. After all, if there are 1000 players in the world, their ability to change the world must be (approximately) 1/1000th of what can be changed. Furthermore, with so many "cooks in the kitchen", the world tends to be a chaotic place.

The fracturing techniques I described make it easier for players to "suspend their disbelief" and pretend their actions impact the world. Unfortunately, each technique has its drawbacks, some of which are psychologically disturbing.

 

 


 

Sympathetic goals

(Back to TOC)

13 September 2005

by Mike Rozak

In Choice and consequences I claimed that quests and goals were just about the same thing, and that it might be better to think of handing out quests to players as handing out goals. Following this idea reveals an important issue: You can't give a player a goal; You can tell them about a goal, but they must decide that a goal is important (to them) before they really accept it.

Thus, a game needs to package the goal so that the player takes up the goal. How can a game produce such packaging?

Standard ways of creating sympathetic goals

When a player is approached by a NPC and asked to collect six what-cha-ma-call-its in wherever land, why should the player actually care?

A run-of-the-mill adventure game, CRPG, or MMORPG, when it's boiled down, provides the following motivations for completing the game:

More ingenious ways of creating sympathetic goals

Now that I've written down the most common techniques for creating sympathetic goals, it's easy to see how inane quest design is in most games.

Here are some better solutions I've seen or heard of. Notice how they involve the player internalising the goal handed out by the game:

Goals already existing within the player

Some goals are already held by the player, and only need to be reinforced by the game:

Conclusion

You may have noticed that most of the techniques are also used by storytellers to keep readers interested. You might say that I'm "adding story to my game" using these techniques, but I wouldn't. The term "story" (as well as the term "game") is so overloaded with meaning that I prefer to avoid the words altogether. What I described herein is a component of a story, getting the player/reader to internalise the goals of the game's/story's characters.

Note: Some of the ideas I've listed in here were inspired by fiction writers who authored their own game-design books: Lee Sheldon's "Character development and storytelling for games", and David Freeman's "Creating emotion in games". I wouldn't add either book to my must-read list, but they may provide some more ideas.

Having said that...

... I had to sneak genre in somehow.

 

 


 

Cost cutting

(Back to TOC)

2 October 2005

by Mike Rozak

Still on the Of mice and elephants topic, I've been thinking about keeping costs low as possible. Here are some cost-cutting measures that I've seen implemented in various games...

The world can be designed to reduce costs:

1.         The action that the player wants to take is often not listed. This happens because (a) the author can't think of every possibility, and (b) even if the author did, it's too expensive to write all the branches.

2.         Branching narration inevitably requires a menu of choices. If players have a menu, they don't have to problem solve (as much) since they're left with a multiple-choice question.

Costs can be reduced using players:

Some less-appreciated cost reducers (aka: players will whinge):

Thinking outside the game:

My suspicion is that games that spread their cost cutting amongst all the techniques will do better than games that just rely on one or two cost-cutting techniques.

 

 


 

Sub-games with variation

(Back to TOC)

2 October 2005

by Mike Rozak

In previous articles, I mentioned that sub-games should vary slightly each time they're used. I thought I'd explain the idea a bit more using the combat sub-game from the Dungeons & Dragons pen-and-paper RPG as an example.

How does D&D vary combat? Take, for example, an encounter with orcs:

Meanwhile, the player characters are gaining new powers:

Basically, D&D starts off 1st level characters with a very simple combat sub-game, and gradually introduces more choices, strategy, and complications into the sub-game as characters advance levels. Since new players begin as 1st level characters and gradually advance, the combat sub-game's complexity rises in line with the player's mastery of the rules. This design ensures that new players aren't overwhelmed with choice and complexity, and that experienced players aren't bored.

 

 


 

The law of new inventions

(Back to TOC)

2 October 2005

by Mike Rozak

Imagine that you have an idea for a new invention and that you want to get people's feedback about the new invention. What will happen?

To make the abstract case more concrete, imagine that it's the 1940s and you figured out how to build a microwave oven. You haven't built one yet, since the R&D is pretty expensive. However, you get together a focus group of people and ask them if they think a microwave oven would be a good idea. In order to get the best responses, you select people that use conventional ovens for your focus group.

What kind of feedback will the give?

You can spend years trying to convince members of the focus group (or the general population) that a microwave oven can be built, and that uses will exist for it. What you'll discover is:

You eventually give up trying to convince people that microwaves will work, and build a prototype yourself. You present the prototype to your focus group, only to discover:

Does this sound too cynical?

 

 


 

My current "grand unified theory" of avatar games

(Back to TOC)

2 October 2005

by Mike Rozak

I've been trying to figure out what adventure games and MMORPGs "are all about". When I originally broached the question to myself, I thought the two were fairly different beasts, and that first-person shooters were from a different planet. I'm not so sure any more...

"Avatar" games

Adventure games, computer role-playing games, MMORPGs, first person shooters, and modern platformers all fit into a category of game that I'm calling an "avatar game". (Anyone have a better name?) The characteristics of the game category are:

1.         The game's physics are very similar to reality; Apples fall to the ground, objects break when they're hit by a large force, and characters look like people or animals. The advantage of basing gameplay on reality is that players already know what reality is like, so they automatically the basic rules of the game; all the game has to teach the player is how to translate their intent into keystrokes and joystick movements, and how the game world's physics differ from real physics. Additionally, when a game world is closely linked to reality, it's easier to escape into.

Conversely, chess, go, card games, and tic-tac-toe have a physics that's very different to reality. Racing games, flight simulators, and sports games are in the grey middle, since they're based on reality, but only on a very limited slice of reality.

2.         The player controls a single character that is an extension of the player within the world, although occasionally games stretch this to a few characters.

Real-time strategy games are different because the player controls an army. A pet-raising game isn't an avatar game because the player doesn't control his character directly.

Sub-games

Within the world (and associated physics), players uses their characters to participate in sub-games. The most common sub-game is combat, but sub-games also include jumping over obstacles, climbing, solving puzzles, talking to NPCs, etc. See Virtual world as platform.

Some important characteristics about sub-games are:

As I noted in Choice, sub-games can be strung together to form quests...

Quests

A quest includes a goal, problem solving, and a series of sub-games that a player must complete to achieve the goal. The goal is tightly tied into the quest, and usually handed out by the game world, although sometimes games (like MMORPGs) let the player effectively create their own quests, deciding their own goals and determining the actions (sub-games) that must be completed to achieve their goals. A well designed quest leads to follow-on goals.

An example of a quest would be:

Quests are used to keep sub-games "fun" since collecting a bag of cherries in order to help a kind old lady is more "fun" than collecting cherries for the hell of it. Quests also tie consequences into the sub-games. See Choice and consequences and Sympathetic goals.

Quests can be used as sub-quests in larger quests.

As I point out in The four pillars, the sub-games listed for a quest represent optimum solutions, and players should (theoretically) be able to approach the problem however they like, including growing their own cherry trees and waiting a few years for the first cherries to appear.

Meaningful choices

The experience must be overflowing with meaningful choices. (See Choice and Choice 2.) These include:

Story

I hate to use the term "story" because it's so loaded with meaning, but some elements of story come into play:

How games differ

As I said at the beginning, a large variety of games are avatar games. They merely differ in their emphasis:

Having said that, some virtual worlds, like Second Life, can only be fit into this grand-unified theory with much contortion. Racing games, flight simulators, and sporting games are in a grey zone too; their slice of reality is so limited that quests are difficult to invent.

 

 


 

The four pillars

(Back to TOC)

2 October 2005

by Mike Rozak

The writeup for my Grand unified theory begins at the smallest components of avatar games, the sub-games available, and works its way up into quests and goals. While refining the writeup, I considered whether it was possible to start with a large view of the world and work down, and whether the same results occurred.

Working from goals on down

Imagine that you create a world...

Why do players visit this world? What do they do? (As Chris Crawford likes to ask.)

I have played plenty of games with the same "what". How about "why?"

The "Why" question leads to goals... Players kill monsters "to save the princess", "so they feel like they're accomplishing something", or "just to kill time." You can read more about goals in Sympathetic Goals. Following this idea, a world must either allow players to fulfil goals that the players bring into the game, or provide the players with goals (that are internalised by the players) once they enter.

It would be easy for me to write an Eliza-like problem that asks players what goal they would like to fulfil and then display, "Poof! You fulfilled your goal." Technically, my "world" lets players fulfil their goals. However, there are some technicalities:

Thus, the world needs to include a set of physics that allows players to complete their goal(s). There are some caveats about the physics too:

Etcetera... Basically, the actions (mostly) correspond to sub-games. I already discussed some of the requirements of sub-games in My current grand unified theory of avatar games.

Mix all the ingredients together, and behold: The same "grand unified theory" reappears... Except, approaching from this direction reveals that players don't have to be lead through sub-games by the nose, as my other GUT paper described.

Q. E. D.

The four pillars

Now that I have clarified that point, keep "goals" and "sub-games" in mind, while I discuss a marketing topic.

At the moment, there are two commonly accepted "rules" for making a successful MMORPG:

You can create a world with great eye candy and lots of socialisation; it's called a graphical chat room. Graphical chat rooms don't do that well financially.

I think that two other "rules" for a successful MMORPG exist. I've read/heard books/people discussing these other rules only in vague terms, while "eye candy" and "socialisation" are clearly articulated, perhaps because the other rules are understood on an intuitive level. Despite being unspoken, successful MMORPGs clearly follow these two rules:

 

 


 

A tangle

(Back to TOC)

2 October 2005

by Mike Rozak

When I consider my grand unified theory, a visual metaphor comes to my mind...

1.         In Choice I gave each type of sub-game a single-letter moniker, like "K" for combat, or "N" for narration. DNA is likewise notated using single-litter terms for bases, GATC, which are graphically represented as different colours on the double helix.

2.         At its simplest, a quest is a series of sub-games, just as DNA is a series of bases. However, choices exist within the quest, so (unlike DNA) there can be branches within the sequence. For example: NcNSKNGN (CX|F) KN allows players a choice between "CX" and "F". Keep a vision of a DNA strand with reconnecting branches (loops) in your mind.

3.         A quest can be a sub-quest of a larger quest. Again, just as like genes (sequences of DNA that produce a specific protein) are combined in series into chromosomes, quests can be combined in series. Zoom your imagination out of the small strand of branching/looping DNA to see a larger picture with a long strand of quest DNA.

4.         Players also have a choice of quests. Quest arcs are tied to one another at the beginning and end. Again, zooming out reveals more even more structure to the quest DNA, producing a tangled mass.

5.         Players' decisions can affect other quests; if the old woman is fed to the troll, she can't bake the cherry pie, and the player won't be able to meet the mayor in quite the same way. You could visualise this by producing more tangles, but there's an easier way: Every time the player makes a choice, the quest DNA rearranges itself, detaching and reattaching portions.

6.         Zooming out completely reveals the big picture. Strands of branching/looping quest-DNA are entangled together. Pieces occasionally detach and reattach as players achieve goals that let them reorder the world. And, most importantly, from this angle, you realise that the bases (sub-games) all have subtle variations in their coloration, since the sub-games vary slightly according to the quest. Fighting a troll is different than fighting an orc because trolls and orcs use different combat tactics, fight in different sized groups, and have different special abilities and weaknesses.

7.         If the exact same colour (sub-game with no variation) is re-used, players will notice and not enjoy the experience as much. If too many of the same colour (sub-game) occur in sequence, or too close together in the strand, players will notice. If there's a pattern to the colours (blue always following red), players will notice. If there's a pattern to the branching, players will notice. If there's a pattern to the way that the quest DNA rearranges itself, players will notice.

I suspect that any identifiable pattern will weaken the experience. However, pure chaos won't work because that breaks characteristic #1 of avatar games, that the world and physics resemble reality. Some sort of balance must be reached between pattern and chaos... which harks back to Raph Koster's Theory of Fun.

 

 


 

The peacock

(Back to TOC)

16 October 2005

by Mike Rozak

I ranted about the need for choice in several articles, such as Choice and consequences. Unfortunately, choices all come to naught when players reach the end of the game, where they have to kill the evil overlord whether they like it or not. All the choices they were given, such as whether to play a good or evil character, what clothes to wear, etc. are completely ignored by the ending. (If your world has no end, such as a typical MMORPG, then this issue isn't much of a problem, although you have a different set of problems to deal with.)

Some games try to solve this problem by allowing multiple endings; In one ending, the player slays the evil overlord, while in another, the evil overlord slays the player's character... I'm being a bit cynical. Some games allow for completely different endings, such a letting the player partner with the evil overlord.

Unfortunately, since designers don't want players to miss too much expensively-produced content, designers inevitably place the branches to the different endings at the very end of the game, usually in the last five minutes of play. This enables players to reload previous saves and see all the endings, thereby making the investment in content worthwhile.

Designers could place the branches earlier in the game, but then they'd need to produce more content to cover the different (and lengthy) branches. Since most players don't replay their games, they wouldn't experience the extra content, making it a waste of resources. Additionally, since we're talking branches here, once a player makes the choice, its effects are final and irreversible. As I pointed out in Choice 2, players will have entered a new pearl on the string/tree. While this makes for a very strong choice, it will annoy many players when they realise they're following the "wrong path" and can't undo it. (The "wrong path" is in the eye of the beholder.)

The feather

Here's an alternative way to view a typical game's choices:

1.         When the game first begins, choices are intentionally limited so that players aren't overwhelmed.

2.         Players are presented with the most choices during the middle of the game.

3.         Since the end of the game is fixed (killing the evil overlord), and usually punctuated by an expensive cut scene, the player's choices are paired down at the end.

If you simultaneously stretch your imagination and squint very hard, you can imagine the game's choices look like a feather, which is narrow at one end (where it attaches to the bird), wide in the middle, and narrow at the tip. (For Monty Python fans, you can also think of it as a brontosaurus.)

When designers produce multiple endings, they usually produce one main feather. At its tip are splayed several other feathers whose roots all meet at the end of the first feather. This configuration happens to look like a tree, with branches where the feathers meet. If you really-really squint, the feathers look like the pearls mentioned in Choice 2.

The peacock

And now for something completely different...

Rather than producing a tree from the feathers, arrange them like a peacock's tail feathers. They all start from one point and splay out in all directions.

Notice that the feathers overlap near the base, so that from a distance, you're not really sure which feather you're seeing. The overlap is greater at the base than the tips. Only at the tips is it easy to identify which feather is creating the pattern. (By the way, peacock feathers really don't overlap that much; Analogies only go so far.)

If I explain the analogy in game terms:

In technical terms:

Notice that this is different from the string/tree of pearls that I discussed in Choice 2. There are no 100% final decisions, although some decisions might be very difficult to counteract.

Advantages and disadvantages of the peacock

The peacock works well because:

In a peacock arrangement, players don't really know what other players' goals are. (The players might not even know their own goals/ending themselves.) Interaction amongst players becomes more complicated:

Of course, the peacock arrangement is not without problems:

Beyond the peacock

 

 


 

Analysis of Peter Molyneux's Fable

(Back to TOC)

20 October 2005

by Mike Rozak

Not owning an X-Box, I have been waiting months for Fable: The Lost Chapters to come out for the PC. I just got my hands on the game last week. This writeup is an analysis of Fable's design, not a review. The article is about "why" Fable works, and why it doesn't.

If you are looking for review, don't read this, since the article not only gives away the plot, it intentionally strips the "magic" away from the game to reveal "the man behind the curtain."

Story-like devices

The most notable difference between Fable and other PC-based CRPGs is the emphasis on "story". I don't like the term story because it's so overloaded with meaning, so when I do discuss elements of story as they appear in Fable, I'll try to use more specific terms, such as narration or foreshadowing.

Let me begin at the beginning...

Act 1a of Fable, first thirty minutes, involves an introductory cut-scene, a short tutorial, a few short quests that the boy-hero must perform, and a the finale where the town is pillaged by bandits and the boy-hero is left to wander around the smouldering ruins.

Act 1a uses a number of interesting techniques that I mentioned in Sympathetic goals:

1.         The introductory cut-scene foreshadows that the boy-hero will go on to do great things, thereby informing the player that he, as the hero's controller, will also do great things.

2.         Act 1a emotionally ties the player into the game, introducing the player to the boy's father, travelling mother, sister, the town, and its other inhabitants. Almost all of these characters (including the town) are presented in a idyllic light, just as Tolkien portrays The Shire as a rural utopia. Having grown up in their own family, players readily identify with a family, particularly an idealised one in a Leave-it-to-Beaver style village. This identification is important since it's later used to provide the player sympathetic goals that keep him interested in the game.

3.         Act 1a serves as the first part of the game's tutorial.

4.         Act 1a culminates with a cut-scene of bandits (their legs only, from a child's perspective) burning the village. The player is then allowed to wander around his burning rural utopia, meet his dying father, and be whisked away by a mysterious mentor.

Most games would just produce one large cut scene of the pillaging and cut "Feature #43: Wander around burning village" for cost and schedule reasons. However, allowing the player to explore his burning village increases the player's sympathy for the hero, and the player's animosity towards the bandits.

Act 1b involves the player's life in the guild hall. It serves the following purposes:

1.         It is the second part of the game's tutorial.

The game includes several other small tutorials later, which is a nice touch. For example: The sneaking tutorial is provided when the player has to sneak into the bandit camp. I particularly liked the teacher who gives the player a quest to find books for the teacher to read to his classroom. When the teacher is given the books, he gives a short reading to the class, as well as the player, thereby feeding the player backstory in the guise of a quest.

2.         The player is introduced to two mentors, the mysterious hero that rescued him, and the guild-master. The cut scenes are designed to imbue the player with the feeling that the two mentors should be thanked for rescuing him, looked up to, and trusted. When the two NPCs assign quests the player, the player is more likely to internalise the quests' goals from his respected mentors than other NPCs. The mentor-apprentice relationship is further emphasised when the guild-master's voice is used to provide occasional tips throughout gameplay, like "Get your combat multiplier up."

3.         The player is introduced to a female classmate who grows up along side him. A friendly rivalry exists with the classmate. It first shows itself during a training duel where the classmate's achiever-father shows up and scolds her for losing to the the player; this makes the player feel guilty for winning, and further ties him emotionally to her. She reappears later both as an opponent and ally. The classmate is used as a device to emotionally involve the player in quests, and to provide a non-lethal rival.

Throughout the game, there is an overriding arc of "story" that is used to keep the player personally involved with the quests:

1.         In Act 1a, the player gets much of his impetus to play from "I just paid $50 for this game and I am going to play it, damn it!" as well as "Look at the pretty eye candy."

2.         The player is sustained through Act 1b by the player's goal to learn the game so the player can take revenge on the bandits.

3.         About a third of the way through the game, the player gets to kill the bandits along with the notorious bandit leader, only to learn that the bandits weren't responsible for the raid. In fact, the evil bandit leader had actually saved his (character's) sister, although the leader still deserves death.

4.         The player is allowed to wander goal-less for awhile, but is soon informed that his (character's) mother is alive and held by the real villain. The player's goal then is to rescue his (character's) mother.

5.         In the act of rescuing his mother, the player (not just the player's character) is captured, imprisoned, humiliated, and tortured. The capture and jailbreak scenes do an excellent job of making the player dislike the villain.

Personally, I discovered that when my escaped character finally got hold of his weapons, I wanted to run through the entire castle and kill all the guards, even though I only needed to kill a few to escape. Then, while running through the tunnels with my mother NPC following, I was much more careful about not letter her get injured than I was with other NPCs. (30 minutes previously, I had the grave digger NPC following me and actually wanted him to get killed by the undead, which they managed to do.)

6.         The final act leads to the death of the real villain. By this point, the player (not just the character) is seeking revenge for his (character's) father, mother, sister, and village, as well as revenge for the humiliation/treatment that the player was put through in jail.

7.         The PC edition adds a new chapter, where the the player must kill the villain yet again. While this extends the game, it doesn't work well as an intrinsic goal because the player has already killed the villain once, already rescued his sister, and already had his mother killed by the villain.

A large story-arc, that runs throughout the entire game, helps the player sympathise with the goals of his character.

Sympathetic goals are also used for the quests. NPCs asking for help are friendly and appreciative of the player. Their requests are reasonable and can only be performed by a hero. They thank the player when the quest is completed. This is in stark contrast to Everquest II where many of the quest givers came off as being aloof, rude, and requesting trivial (non-heroic) jobs to be done.

While a MMORPG can't provide a player with a family to be captured by a personal enemy, many of the tricks used by Fable could help MMORPG players internalise their character's goals.

Other player goals

Players also have the following goals that they bring into the game:

Sub-games

Fable relies primarily on the following sub-games:

1.         Walking - Players spend most of their time walking. Walking has two variations, running and sneaking, that are sometimes required to complete the game; Fable included several race quests where walking becomes the main sub-game, as well as times where sneaking is required.

I expected to have jumping and swimming sub-games to accompany walking, but was annoyed and frustrated by their absence. I would liked to have seen other variations in the walking sub-game, such as walking on ice (low friction), or sneaking on noisy/quiet surfaces.

2.         Combat - Fable's combat is divided into three types:

o    Melee - Close-ranged combat involves clicking the left mouse button to attack. The right mouse button is a "flourish", which allows a character to do more damage after several successful attacks. The middle button is used for parries.

Personally, I didn't use the parry button often because (a) my character was so much stronger than his enemies that I didn't need to, and (b) enemies usually attacked in hoards that came on so quickly that I only had time to furiously click the left button with occasional swipes at the right. Melee combat, while fast and furious, didn't give me the sense of being in control.

o    Archery - Bows are fired by pressing and holding the left mouse button. Aiming, which is required, is aided by a sighting view activated by the right mouse button.

I liked the archery sub-game best because of the control, but used it the least because it was only practically useful for one or two shots before the hoard of enemies would set upon my character, requiring melee or magic.

o    Magic - Of course, Fable includes lightning bolts, fireballs, and healing spells. Fable also has some more innovative spells like one that slows time for enemies. As with most CRPGs, spells are mana based, which has design downsides. See Sub-games with variations.

Magic's fun-level placed it between melee (boring) and archery (fun). Again, I suspect control was a factor in "fun", since I had several relevant spell choices at any one time, which is more choice/control than combat, but less than archery.

While it's possible for characters to specialise, combat design seems to encourage an initial shot or two with a bow (or long-range spell), followed by furious left/right melee clicking and occasional spells such as time slowdown or healing. (Throw in an occasional NPC-follower to rescue.) Since every combat experience requires at least some melee, archery, and magic, combat remains interesting throughout the game, which is more than I can say for MMORPG combat.

Unlike most CRPGs, combat did not evolve significantly as the character became more powerful. Fable uses a skill-based system with no skill tree, allowing characters to acquire all the spells and feats early on. As a character becomes more powerful, the spells/feats do too. However, from the player's perspective, they're given a lot of new toys (spells and feats) near the beginning of the game, and then nothing for the remainder; it's like having your birthday on the same day as Christmas.

However, Fable used extensive variation in monsters and settings. Some variations that worked particularly well were:

o    Archery competitions - Combat against a moving dummy.

o    Rock-hurling burrowing troll - As the character leads a NPC out of the wilderness, a cut scene shows a troll erupt from the ground and hurl a stone at the PC. When the PC ducks for cover, the troll burries itself again until the PC exposes himself. This means the PC must aim an arrow at where the troll is buried, wait for it to come out, shoot the arrow, and duck before the troll hurls its next boulder.

o    Fighting the bandit boss - Two interesting tricks are used here. First, the arena is ringed by bandits with swords. They merely stand and cheer the bandit leader until the PC is pushed too close to them, and then they swing their swords at the PC doing damage. When the player manages to get away from the ring's edge, the bandits stop swinging. Second, the bandit boss can't be hit until he manages to get both swords stuck in the ground, which he does every 10 seconds or so.

o    Arena combat - Wave upon wave of monsters appear in an arena as a crowd cheers. The arena also includes damaging machines that are good to push monsters into.

o    Invincible monsters - Some monsters, such as the fairies/nymphs, can only be killed when they're in fairy form, not when they're a ball of light.

o    Timed fights - Some combats must be completed in a fixed amount of time.

3.         Leading NPCs around - Players spend a lot of time leading NPCs from one place to another. Fable's implimention is more fun than other CRPGs and MMORPGs that I've played because:

o    NPCs have a habit of walking into the middle of enemies and need rescuing. In a few cases, the enemies intentionally ignored my PC and went after the NPCs.

o    NPCs make comments about the player's ability to lead, and produce emotes that show how frightened they are. It's amazing how much the comments add to the experience.

o    There are several variations to the sub-game: Sometimes the NPCs will help fight. Sometimes there are several NPCs to lead. Sometimes the NPCs do the leading. Some NPCs must be found first.

4.         Experience point management - Since Fable is skill-based, players need to decide how many experience points to put into a skill.

5.         Inventory management - Simply put, there is no inventory management. Fable allows characters to carry as much as they want, so players never have to decide what they wish to keep or throw away.

6.         Buying and selling equipment - This is a non-event too. Most treasure from monsters and chests is coin or health/healing potions. Plus, half way through the game, the character has so much money lying around that the player doesn't need to sell loot.

7.         Searching - Items that can be searched or interacted with turn blue. As is the norm with CRPGs, searching wasn't very impressive.

8.         Digging - Digging with a shovel is a version of searching where hidden items aren't highlighted in blue. It's used sparingly to good effect.

9.         Stealing - Stealing uses the same mechanism as searching, except that players don't want their character to be seen by any NPCs. An icon informs players how many NPCs are looking though.

10.      Puzzles - As with most CRPGs, Fable didn't have many puzzles. Those that it does have include readily-available in-game hints.

Fable also includes some other sub-games that don't seem to "fit" because they don't have anything to do with the goals I had internalised, namely revenge and finding/rescuing my character's family. These sub-games would work better in a world-like MMORPG than a directed experience like Fable:

A quick comment about marriage: From the beginning of the game, I knew that the way I'd "win" would be by killing the main villain. My knowledge/expectation of this completely emasculated many of the sub-games, like marriage. If the game were posed as one of social/political manoeuvring, where dressing fancy and having a wife at your side was a viable way to defeat the villain, then many of the sub-games would stop being mere diversions.

A quick comment about balance: To be saleable to a non-CRPG player, Fable is designed to be easy, handing out copious amounts of treasure and loot. However, as I pointed out in The four pillars, if a player can win the game using only the walking sub-game (without using the other sub-games) then they will only every try the walking sub-game, and then later complain the entire game was boring. The combat sub-game provided my character with so much money and XP that I didn't need the other sub-games to make money, like trading, gambling, and real estate.

Choices

Fable's USP (unique selling point) is the ability for players to choose their destiny, allowing them to play their characters as good or evil.

Fable provides the following choices to players:

Fable is very weak on other choices:

User interface

The UI is atrocious. It is difficult to get around, doesn't have enough customizable tool-bar items, doesn't use standard PC conventions (such as "i" for inventory), and overloads the mouse buttons. (The right mouse button's meaning changes between "running" and "target arrow".)

Several reasons exist for the UI's failure:

 

 


 

More thoughts on the strengths of text

(Back to TOC)

15 November 2005

by Mike Rozak

Here are some more thoughts on the strengths of a text/verbal world, as opposed to a 3D eye-candy centric world:

Some other strengths keep reoccurring every time I reconsider the issue:

For previous articles, see Of mice and elephants and Small VW operators vs. large, and Text vs. graphics.

 

 


 

Avoiding elves, orcs, and hobbits

(Back to TOC)

15 November 2005

by Mike Rozak

If you were to invent a world, how could you break out of the stereotypical fantasy mold? Here's a brief survey of different aspects of worlds that might help.

Physics

When you create the physics of your world, you have four choices:

"Some magic" and "High magic" seem to attract the most players. Real-world physics is probably too boring (for the players attracted to today's games) and dreamlike worlds are just too weird.

Races

The standard fantasy (and science fiction) game is based around humans. Sure, some of the humans have pointy ears (elves), others are ugly and mean (orcs), and some a short (hobbits). In the end, they're humans, just like Star Trek's Klingons (ugly and mean) and Romulans (pointy ears) are human.

Living in a homo-centric urbanised world, most people probably don't understand the point I'm trying to make. Therefore, I'll explain some non-human alternatives. (Most people still won't understand what I'm saying, but then again, you can't explain colour to someone who has been blind their whole life. Unless you've dealt with a number of different animal species, you can't quite understand how different species react differently and how their personalities differ from people's.)

If I take one step away from humans, I come up with two new types of races:

Ultimately, humanoid animals and androids feel and play like humans. They have a head, two arms, two feet, and can talk. They are mass-market fare, and using them in a design is a safe option.

If you want to get more radical, you can include:

I suspect that if you base your world on any of the above races you'll wind up with a very small market; In the 1980's I bought "Bunnies and Burrows", a pen-and-paper role-playing game based on Watership Down. One of my friends got so frustrated with the experience of role playing a rabbit (no hands, no intelligence) that he made his character go crazy and attack the rest of the party.

If you're really brave (and want practically no players), you could experiment with even more un-human races:

Technology

Technology is what the inhabitants of the world can create. You basically have the following choices when it comes to technology:

Setting

The most common locations in virtual worlds are:

Less common, but still acceptable are:

Some really bizarre ones are:

The more bizarre the location, the less that players will be able to empathise or even comprehend it, and the fewer players there'll be.

Cultures

The races of the world can have different political systems:

Likewise, you could include:

Most worlds limit themselves to "nothing offensive" and have no defined political system or culture. Basing a world on a theocracy might show players what it's like to live in Iran, but they wouldn't play for very long.

World events

What's happening in the segment of the world that the players occupy?

What players do and why they do it

What do players do in the world?

And why do they do it?

The cliche...

A standard MMORPG or CRPG is based on a world with:

Thus, The game with a thousand faces.

 

 


 

When I was a child

(Back to TOC)

15 November 2005

by Mike Rozak

When I was a child, I spoke as child, I understood as a child, I thought as a child; but when I became a man, I put away my childish things.

1 Corinthians 13:11

When I first began playing CRPGs and text adventure games in the early 1980's, the games basically consisted of:

1.         A virtual space to wander around.

2.         A collection of unrelated puzzles or unrelated of monsters to kill.

Hunt the Wumpus, Zork, The Temple of Apshi, and Ultima I all basically followed this model. They had not rhyme or reason to their existence, but they were fun (at the time).

As I grew older, I wanted more from my games:

1.         Backstory that explained why the puzzles and monsters were there.

2.         A variety of sub-games.

3.         Variation in sub-games.

4.         A huge world.

5.         Eye candy.

Wizardry and Ultima II provided these features, and I was satisfied for a time.

I didn't play many games while I attended university, nor when I first started working. When I next explored CRPG and adventure games, I discovered some additional features had been added to the cannon:

1.         Quests.

2.         Cut scenes.

3.         Every game had a few new twists to old features.

4.         The ability to play online with one's friends.

Again, I was satisfied for a time, but now I have played enough of those games. I want some new dimensions added to my gaming experience:

1.         Meaningful choices.

2.         Sympathetic goals; I want to feel that world is important to me, not just important to my character.

3.         A small and focused world. I don't want to spend 50 hours playing a game, let alone 500.

Games with these new features are starting to appear; I am not yet satisfied.

A curious question keeps creeping into my head... When I was a child I couldn't understand how I would see the world an adult. As an "adult", I can't understand how I will perceive the world in the future. Will I want even more intricate worlds? Or, in other words: All of the above features are "necessary" for a game, but are they "sufficient"?

Should CRPGs, MMORPGs, and adventure games also involve:

1.         Real AI?

2.         A director/God?

3.         A theme?

4.         Something else that I can't even imagine with my child-like mind?

 

 


 

Personal NPCs

or... What The Sims Online could have been

(Back to TOC)

15 November 2005

by Mike Rozak

When you're lying in bed at night, the reading light on, novel in hand, and reach the end of a chapter, what makes you say to yourself, "I'll read just one more chapter, and then I'll go to sleep"?

Likewise, when playing a CRPG or MMORPG, what makes you want to play for "just another ten minutes so I can finish off this quest"?

Summary of previous articles

As I stated in Sympathetic goals, players play CRPGs, adventure games, and MMORPGs for a variety of reasons, most of which are ultimately exploited by the game's design so that players keep playing. (Novels take the same approach, doing whatever is necessary to keep readers from putting down the book.)The most common techniques that games use are:

Another reason for playing is less commonly utilised by MMORPGs: Players want to complete quests that affect NPCs which the player (not just the player's character) likes or dislikes. I discussed this idea in My current grand unified theory of avatar games, as well as Sympathetic goals. A few weeks after writing the articles, I purchased Fable: The Lost Chapters, and discovered Fable practicing some of the techniques I was theorising about.

Fable, more than any game I've played, went out of its way to make the player (not just the player's character) like a handful of NPCs, and dislike the villain. The game did this by creating a family for the player's character, and integrating the player (not just the character) into the family. The villain then burns the character's village in front of the player's eyes, culminating with the father's death just as the player finds him lying in the burning village. This creation of sympathy/empathy continues throughout the game, and is an important technique for keeping the player playing. See my Analysis of fable.

Translating sympathetic goals to MMORPGs

In GUT, I used an example of an old woman asking for help as a way to produce a sympathetic goal. While this works to align the player's goals with the NPCs, it is limited because:

1.         The old woman character can only be used for a few quests. In a MMORPG, most NPCs hand out only one quest. A few NPCs hand out as many as five quests before they're "used up". Either the NPC has no more logical quests left to hand out, or the player's character becomes so powerful that he no longer frequents the static location where the NPC stands.

2.         No matter what a player does to help the old woman, she will always be standing on the street corner soliciting help (from other players) for her cherry quest. She is too static.

3.         In a problem unique to MMORPGs, other players will also have completed the woman's quests, which is fine for picking berries, but problematical for heroic deeds like saving the woman's life. For one, any NPC whose life needs saving 250 times a day probably isn't worth rescuing. Second, a player cannot fail to save the woman's life because that would mean no-one else could undertake the quest ever again. These limitations weaken the experience, and further objectify the NPC.

Fable produces its sympathetic goals using a home town, father, mother, sister, mentor, rival, bandit leader, and arch-villain. All of these characters, or their memories, re-occur within the game and produce ties that keep the player completing "just one more quest".

The character archetypes that Fable employs cannot be used in a MMORPG because:

1.         All of the above reasons.

2.         Players will find it very improbable that each of their characters grew up in the same town, all had fathers that died, and sisters that were kidnapped, etc.

3.         When players group together to help each other with quests, which is one of the reasons why MMORPGs work, they will end up rescuing the same sister or killing the same villain over and over.

So how can Fable's use of sympathetic goals be accomplished in a MMORPG?

Personal NPCs

Players need to have personal NPCs. Personal NPCs are NPCs that exist only when players log on, and that are somehow tied to the player's character.

Personal NPCs already exist in many MMORPGs; they're called pets. Some obvious archetypes for personal NPCs are:

Some less obvious "NPCs" follow: (Their utility will become obvious later on.)

Each archetype should provide several different "flavours" to choose from... Not all pets should use Lassie's AI and storyline; some are rescuers, while others are chicken killers. Providing a number of flavours gives players choice.

In a MMORPG, multiple flavours are especially important, ensuring that player A's villain is not the same as player B's villain. Obviously, the two villains will be given different randomly-generated names and appearances. That's not enough. They must also be provided different personalities and methods of villainy.

Due to real-world development costs, I suspect most archetypes will have around five flavours, so there's a 20% chance that player A's villain will be awfully similar to player B's villain. (Maybe both villains attended the same school of villainy. :-) )

Quests handed out by personal NPCs

In a contemporary MMORPG or CRPG, if a player purchases a pet, the pet is used as an extension to the player's combat or travel skills, nothing more.

In real life, if you purchase a pet you get:

Think of these extras as "quests"...

Personal NPCs are really quest givers. They provide goals for players. Grunties (pets in the Hack//Sign anime series) will get sick and need smiling cherries to heal them. Spouses will want to go on a holiday. Children will need trips to be dropped off at school. Henchmen will have personal problems of their own that need solving, with a player's help, of course. Mentors will need supplies for their magical experiments. Houses will have gutters that fall off. Etc.

Personal NPCs are great ways to introduce quests because players have an ongoing relationship with their personal NPCs. A skilled writer can use the ongoing relationship to either make the player like the NPCs, or in the case of villains, rivals, and hoodlums, dislike the NPCs. These relationships create sympathetic goals:

1.         The player (not just the character) wants to complete the quest because he likes/dislikes the personal NPC.

2.         Even if the player doesn't have any emotional attachment, they at least want to keep friendly NPC around and eliminate enemy NPCs. In order to keep the NPC alive/friendly, players need to complete the quest. For example: If a player spends a lot of time levelling up and outfitting a henchman, he doesn't want the henchman to leave in search of the henchman's kidnapped daughter... who may have been kidnapped by the player's villain. The player will volunteer to help the henchman more readily than some Joe off the street with the same quest. Likewise, personal villains need to be eliminated quickly or they'll just return later with new dastardly deeds.

Having personal NPCs hand out quests also provides other benefits:

Some implementation details

For those of you who are technically minded:

Another way to think about the implementation

You can think of each NPC as coming loaded with a series of quests with choices. The choices end up producing a branching narrative (like a Choose Your Own Adventure book) that's interspersed with sub-games (like combat or travel).

By producing a system where several NPCs are "assigned" to a player's character, you have just created a pre-emptive multitasking system. Or, in literature terms, a threaded storyline. Basically, players are following several branching narratives concurrently and can chose which narrative to interact with at any given point. However, any particular narrative is sparingly doled out over a long period of time, ensuring that the player doesn't "overdose" on one personal NPC to the neglect of others. Ensuring that multiple plots/NPCs are running at once also allows them to affect one another, such as a childhood friend running off with the player's spouse.

Conclusion

As I stated earlier, personal NPCs aren't entirely new; many aspects of what I describe have been around for awhile:

 

 


 

Story arcs and quests

(Back to TOC)

22 December 2005

by Mike Rozak

It's now time for me to wrap a number of ideas together...

Quests

Just about every virtual world has quests, and as I've posted many times before, I feel that quests are critically important for the future development of virtual worlds and computer games in general.

To summarise what a quest is:

1.         Players are provided a bit of narration and given a goal to complete. Contemporary MMORPGs usually rely on either "Kill N monsters of type X", "Acquire N items of type Y", or "Deliver item Z to NPC A". Some even include, "Escort NPC B to location C."

2.         The player runs off and completes the actions. A recent (aka: in the last 10 years) feature is to actually show the player their progress, displaying "Only 7 orcs left!" after an orc has been killed, or "Only 3 rat tails left to collect!" after a rat tail has been collected. This automatic tally capability is important, as I'll discuss in a bit.

3.         Once the player has completed all of his tasks, he is either rewarded immediately, or returns to the NPC to receive his reward.

Of course, such a simplified description of a quest is like saying that a novel is merely "about a character that gets into conflict with another character." While the statement is true, it doesn't explain the subtleties that actually make novels interesting, nor does this oversimplification of quests do much to explain their potential.

Since I've discussed the potential of quests many times elsewhere, I want to talk about the "automatic tally capability".

Code associated with quests

When a player kills an orc for a quest, most MMORPGs increment a counter in a structure (or database) that's linked to the character's instance of the quest. The, "You have 4 orcs left to kill!" message is displayed. When the counter reaches a preset value, the quest is done, and some code executes that says, "You finished your quest!"

In most MMORPGs, quests are NOT C++/programming objects. They are database entries with associated "global" code for counting quest-related actions. What quests they were objects?

Imagine that whenever a player undertakes a quest, a new "quest" object is created. That object attaches itself to the player character and "goes wherever the player's character goes". It sits silently and watches what the player character does. When an orc is killed, the object registers this fact, increments a counter, and displays, "You have only 4 orcs left to kill!"

Ultimately, this is more work. What does it get you?

1.         All the code for a specific quest is nicely encapsulated.

2.         All "FedEx" quests are merely sub-classes of the master FedEx class. All KillNMonsters are merely sub-classes of the master KillNMonsters class.

3.         Something wonderful... Turning quests into objects unifies many of the concepts I have discussed in previous articles.

Object + Quest = Story arc

Since I want to distinguish "Object + Quest" from just quests, I'll call "Object + Quest" story arcs.

A story arc easily enables the following ideas:

Some random thoughts...

Story arcs could easily perform the following tasks:

You should allow players to turn off their story arcs if they don't wish to be manipulated; Theoretically, a story arc will make the experience more fun though, so players will want to have story arcs enabled.

Some deeply random thoughts...

1.         Players explore/experience a physical world constructed from three dimensions, landscapes, dungeons, puzzles, traps, etc. (This is adventure game and CRPG fare.)

2.         When players talk to NPCs, they explore a mental and social world, manoeuvring their way through conversations, social ties, personality conflicts, etc. You could imagine, for example, that taking part in a conversation or trying to climb a social ladder is like wandering through a maze of twisty passage, all alike. (Chris Crawford's AI is working in this direction. CRPGs have NPCs that aren't interacted with on a mental/social level.)

3.         Story arcs let players explore a relational world, where choices and actions have long-term ramifications. Choosing to slay 10 orcs means that ogres will take over their abandoned stronghold later. Story arcs can be used to explore consequences.

 

 


 

Storylines II

(Back to TOC)

22 December 2005

by Mike Rozak

In Intertwined storylines, I talked about the ability for a virtual worlds to cater to several different "storylines", the most obvious being World of Warcraft's Hoard versus Alliance, or good versus evil, etc. However, more complex storylines are also possible. I will spend this topic discussing some more storyline ideas.

But first, I want to illustrate why storylines are important:

Stories, single-player games, and virtual worlds

In a linear story, you can have: Bob, a farmer in 1930's Okalahoma, experience the dust bowl. Over the course of the book, Bob does X, Y, and Z, with the aid of his loyal wife.

Basically, in a linear story, the author controls:

A single-player game can:

In a virtual world, designers can (or cannot):

Part of the reason why I wanted to mention this transition from story to virtual world is to point out that virtual world designers cannot control many of the aspects of an experience that linear authors control, or even that single-player game designers control. Not being able to control the exact order of action, X, Y, and Z, that a story author controls, means that coincidences can't (easily) happen in a virtual world, a crutch that many authors rely on.

If a designer can't rely on linearity to make the player's experience "fun and enjoyable", he needs other tools and devices. One of the devices that virtual-world designers have that story authors and single-player game designers don't have, is the ability to enmesh other players in the player's experience. Of course, this is an obvious statement, but it's worth pointing out.

Why virtual worlds need many storylines

One very important "crutch" that single-player game designers rely upon is the ability to specify the player character's storyline, which is basically the character's role, what the character does, and why the character does it.

A story or a single-player game can get away with only one storyline. If a virtual world tries to rely upon one story line (by making all players 1930's farmers, for example), then they run into the following problems:

1.         As a player, when you encounter another player, you automatically know that the other player is a farmer.

2.         If you know he is a farmer, and following the same storyline, then you know what the other player has already done and/or will do, since they're experiencing the same basic set of events as you are.

3.         This makes you feel like you're on a ride at Disneyland, and have no real control over your destiny.

4.         You have an incentive to always work with other players to accomplish a task, since they have the same goal.

5.         A clever designer makes some sub-games/quests zero-sum, so that you win at the expense of another player losing. Thus, some encounters with other players result in conflict.

6.         Even with this, most encounters will be win-win or zero-sum. There won't be any ambiguity.

The most popular way to get around this problem is to:

1.         Define the storyline as the character's growth from a 90 pound weakling to a super hero... Player characters start out as pathetic level 1 newbies and gradually work their way to level 100. This is called the "Hero's Journey".

2.         The world is divided into zones. Zones have monsters placed in them so that they encourage level N characters to visit the zone. Low-level characters won't enter a high level zone because they'll be slaughtered right away. High-level characters won't enter a low-level zone because they won't earn enough XP and loot for their time investment.

3.         Of course, as characters kill monsters, they get XP and loot, which makes them more powerful, and eventually encourages them to leave their current zone to visit a higher-level zone.

4.         If a world hands out XP and loot slowly, players will be forced to visit every zone and complete every quest in order to earn enough XP to advance to a higher-levelled zone. Players will feel like (a) they spend all their time grinding, and (b) they have no choice in what zones/quests they visit because they have to visit them all.

5.         If a world hands out heaps of XP and loot, players will never visit most zones. They will feel like they have plenty of choice, and won't be grinding, but will be annoyed that they didn't experience all the content. At the same time, management will complain that the designer wasted money producing content that players don't use.

6.         World of Warcraft seems to have a happy medium, allowing players to skip half the zones as they advance. This allows players to chose what quests they will undertake, while not wasting too much content.

The "Hero's Journey" game storyline has been done to death! It works, but it has become very cliche.

Down with levels

Notice the correlation between "levels", "storyline", and the "Hero's Journey". Levels are also linked to other techniques; see Experience points.

Imagine, for a second, that you got rid of experience points and levels... I know, it's difficult to imagine, and induces the frightening feeling of a loss of security. You still have a storyline, but can't use levels as the way to drive the player forward.

Instead, a storyline is a series of quests.

Because players should have choices, players can chose to take different branches within the storyline, which lead to different series of quests. The choices are either explicitly made by the player ("Do you want to open door A or door B?"), implicitly made (Does a player's actions make him good or evil?), or are based on success/failure. (Failing to rescue the princess leads down a different path than success.)

To add another dimension to choice, several quests will be on the player's plate at once. Completing a quest sometimes spawns two quests. Some quests are initiated based on a timer (or random event). Others are purely optional side-quests that player can chose to accept or ignore.

Contemporary MMORPGs use zones with level-N monsters in it to prevent characters from taking quests out of order, or bypassing all the quests and ending the game by killing the evil overlord in the first ten minutes. A level-less storyline system prevents players from skipping (too many) quests by not allowing players to undertake new quests until they have completed prior quests; this may require that certain areas of the world be inaccessible or hidden until the necessary quests are completed.

A level-less storyline has the advantage that friends can help each other out. In MMORPGs, friends might have characters of vastly different levels, preventing them from playing together.

Another way to think about this is to view storylines like a Choose Your Own Adventure book where each page not only involves a choice of A, B, or C, but also a quest that the player needs to complete. The quest inevitably involves sub-games. See The game loop.

If you already read Personal NPCs, you'll notice that the scheme for storyline quests is almost exactly the same as the scheme I proposed for personal NPCs. Bother personal NPCs and storylines can be handled using the same mechanism, and both should exist in a world.

An example storyline for a farmer during the 1930's might be:

1.         Personal NPCs introduced for the character's siblings.

2.         Option to buy a pet. (Acquire a personal NPC).

3.         Buy a horse or a Model T. (Acquire a personal NPC).

4.         Rent or buy a farm. (Acquire a personal NPC. Buildings require maintenance, etc.)

5.         Find a wife. (Acquire a personal NPC.)

6.         Have kids. (Acquire a personal NPC.)

7.         All of the personal NPCs bring their own quests. Meanwhile, the player deal with the trials of the early dust bowl, trying to get crops to grow as well as possible. Have the player experience three to five years, each year getting successively harder and brining new challenges.

8.         Decide to stay or leave. This produces a branch that lets the character to scratch for a living from the dust, or travel to California.

9.         The storyline ends when the player survives the "boss" dust storm or finds a new home in California.

Choice of storyline

As I stated earlier, a single-player game can rely on just one storyline. A virtual world must provide at least a handful of storylines. Some other storylines might be: Be the wife of a farmer, be a child of a farmer, be the banker, be a merchant in town, be a hobo, be a thief, etc.

Some issues arise:

1.         When a player creates a character, they may be given a choice of which storyline to play. Contemporary MMORPGs do something similar by allowing players to select their race and class.

2.         Some storylines may not be available until the player has played through others. For example: A player may not be able to play a banker until he as played a farmer.

3.         Some storylines are "earned", as in The peacock. Maybe the option of becoming a thief or hobo can only be "earned" by the player's actions as a farmer.

4.         When a player finishes a storyline, they may wish to continue playing the same character. At that point, the designer can (a) forcefully terminate the character, (b) allow the player to continue with the character but not do anything except side quests, or (c) transfer the character over to another storyline.

5.         A player might get part way through the farmer storyline and decide he doesn't like being a farmer. The design might allow the player to suddenly (and without any real in-game reason) switch to being a grocery store owner. Or, the design could require that the player start a new character.

6.         As with personal NPCs, each storyline might have several variations. When a player decides to be a farmer, they won't know if they're getting farmer storyline A, B, or C.

7.         Players may opt not with to be a part of any storyline, in which case they can spend their time acquiring personal NPCs, playing optional quests, or chatting.

Typical MMORPGs allow players to chose a class and race, which is sometimes used as a storyline selection technique. In WoW, players that chose to be elves join the alliance storyline, while undead are part of the horde storyline. (Linking race to storyline is somewhat self defeating because everyone in WoW knows that elves are part of the alliance storyline.) Unfortunately, most MMORPGs don't associate class/race with storyline, probably because it requires more content, which means more money.

A designer can obscure levels by replacing them with skills, which are basically a vector of levels. A player that focuses on a specific skill also focuses on a specific storyline; a player that invests in "accounting" will probably follow the banker storyline, while one with "animal handling" might follow the farmer storyline. Star Wars Galaxies does/did this.

The end

My intuition and thinking says that virtual worlds should have an end, as described in the anti-MMORPG. This means that at some point players will be told, "You're done. You might want to leave now."

Unfortunately, storylines are problematical for the following reasons:

The solution is to let players daisy-chain storylines.

If players can daisy-chain storylines, then the overall concept is somewhat weakened. Unfortunately, I can't think of a way around the daisy-chaining. The designer of a single-player game can conceivably say, "You're done. Get out of here." However, this isn't possible/wise with a multiplayer game (as shown above).

In a multiplayer game, daisy-chaining produces a series of mega-quests (aka: the storylines) that can only be played one at a time. The ending of the mega-quest (storyline) is a good time for the player to quit the world, if that is what they wish. Players that quit immediately after a storyline finishes will leave on an up-note; they will have defeated the evil overlord, or rescued the princess. This positive ending will encourage players to recommend the experience to other players.

To use a food analogy, storylines in a multiplayer world are like a link of sausages; players break off as many sausages as they can eat, and leave the rest on the link. Occasional miscreants take only half a sausage, but most people take a whole one. Theoretically, players will take their favourite sausage flavours first (curry, chilli, or rosemary and thyme), and won't bother trying flavours they like less. Most people will eat one or two links in a meal, while some die-hards will go for three or four.

Look ma, no levels!

I haven't gone into detail about all the quests and choices needed to make a farmer storyline, but they're fairly obvious; just imagine you're a farmer in the dust bowl and imagine all the tasks you'd have to do, as well as unfortunate events that might require a response. I can think of oodles. For one: milking the cow. You could make a sub-game out of milking the cow, just don't make the player milk the cow more than a few times or they'll get annoyed with the grind. Milking-the-cow even has variations in the sub-game; the cow may become more difficult to milk as the drought worsens, or a player's only bucket may get damaged and be more easily tipped by the cow.

You can do it all without levels, and without monsters.

Some additional points:

 

 


 

The game loop

(Back to TOC)

22 December 2005

by Mike Rozak

The basic game loop

1.         Goal - Give the player a goal, preferably an internalised goal, either using story-like techniques, or based on a goal that players bring with them to the game. Ideally, provide the player a choice of the goal, and/or the order to achieve the goals.

2.         Problem solving - Provide a problem that the player needs to solve to accomplish the goal. Preferably, provide a choice of solutions. Problem solving also includes investigation.

3.         Action - Provide a mechanism for the player to act on the world, to achieve the goal using the solution he devised. The mechanism involves time commitment, the player's skill, and the risk of failure. Preferably, there are a many subtle choices allowed with every action.

4.         Reward - When the player succeeds (or fails) in the action, provide a reward (or an occasional punishment) based on the goal and the solution. Rewards/punishments usually involve the fulfilment of the goal, although not exactly as the player expects. Rewards/punishments sometimes affect future choices, future goals, the range of solutions available for future problems, and/or the actions used in future. They might simply be parts of a story or eye candy.

5.         Repeat, not necessarily in this order.

The game loop in multiplayer games

1.         Goal that somehow involves other players

o    Friends

§  Want to hang out with friends.

§  Help friends.

o    Guild members

§  Rising in guild rank.

§  Help guild.

o    Neutrals

§  Meet new friends (or enemies).

§  Recruit new guild members.

§  Help other players.

§  Get information or a service from a neutral.

o    Rivals

§  Beat a rival at a contest.

o    Enemies

§  Defeat an enemy player (or guild).

§  Must interact with other players to defeat an enemy player, or help a friend.

2.         Problem solving that somehow involves other players

o    Friends

§  Getting friends in one place and/or convince to work towards a game/multiplayer goal.

§  Dividing loot.

o    Guild members

§  Getting guild members in one place and/or convince to work towards a game/multiplayer goal.

§  Dividing loot.

§  Organization.

o    Neutrals

§  Find a neutral willing to help with a game/multiplayer goal.

§  Determine what the other player wants to improve bartering positions.

o    Rivals

§  Determine how to overcome rival in a contest.

o    Enemies

§  Determine how to overcome an enemy.

3.         Action that involves other players

o    Standard single-player mechanics applied to multiplayer games

§  Combat

§  Influencing NPCs

§  Pickpocket

§  Etc.

o    Multiplayer mechanics

§  Chat.

§  Forums.

§  Bartering.

§  Trading.

§  Guild management.

§  Contests.

4.         Rewards/punishments involving other players

o    Standard single-player rewards/punishments, but gotten/given by other players

§  Loot, experience points

§  Damage, character death, loss of equipment

§  Knowledge

o    Multiplayer-specific rewards/punishments

§  Friendship

§  New guild members

§  Join a guild

§  Networking

§  Rivals

§  Enemies

§  Social status

§  Political power

Note: Mix and match between multiplayer game-loop components and single-player game-loop components. This, a goal might be single-player based, but require a multi-player solution, using single-player actions, producing a multi-player reward. Mixing and matching is important, since it provides more variety in game play (exponentially so) than single-player alone or multiplayer alone.

 

 


 

Dream factory

(Back to TOC)

22 December 2005

by Mike Rozak

In The attraction of impossibility I wrote about people being attracted to virtual worlds (and games) for the ability to do something they can't in real life. The player pyramid discussed how virtual worlds tend to emphasise social goals/desires that people can't get from real life, while single-player games can fulfil non-social goals/desires (or whatever their AI is capable of.)

Richard Bartle's player model in http://www.mud.co.uk/richard/hcds.htm discusses similar ideas, being more minimalist in his categorisation, and claiming that people play virtual worlds because they want to socialise (socialiser), be socially dominant (killers), compete (achievers), and explore the world (explorers). Explorers don't fit my "virtual worlds are socially motivated" theory as well as the other three player types.

Nick Yee has a similar set of "facets" that cover socialisers, killers, and achievers, but leave out explorers.

Basically, Richard Bartle's and Nick Yee's models both come down to the fundamental goals of the player: why the player is playing, often at a subconscious level. My musings came to the same conclusion.

However, games and virtual world can provide other goals, sympathetic goals. These are goals that the game gives to the player, and which the player internalises into his own goals. For example: "Kill the evil overlord" is a goal given by the game world, and which (through various devices) Fable manages to internalise into the player. (Random comment at the end of the article.)

The various player models and sympathetic goals are flip sides of the same coin. Player models describe what goals a player brings into the game/world, while sympathetic goals describe the goals that the players acquire once there.

I suspect that certain players are more strongly attracted to worlds that allow them to achieve their own goals, preferring to join world-like worlds. Other players find it more fun to take one the world's goals, joining game-like worlds.

Revisiting goals/dreams that players bring into the game/world

Since I have recently spent a considerable amount of time looking at sympathetic goals, I thought I'd revisit player models a bit, and fine-tune some of my thoughts from The attraction of impossibility.

To attract players based on their personal goals, a game design needs to resonate with their goals. Obviously, a world accurately based on the Roman Empire would attract players who are interested in the accurate depiction of the Roman Empire... all 10,000 of them. A world that allows players to be a cowboy might attract wild-west fans. Etc.

A niche-market world can choose a specific interest group and tailor its experience to the group, particularly if the group can't achieve its goals in real life. The specific features are, of course, dependent upon the group. People who are interested in realistic space travel might want a realistic simulator of travel to and then explore mars (that would bore the pants off most people).

What does a game/world do if it wants to attract a larger (mass-market) audience? Richard Bartle's and Nick Yee's models provide some insight, although I find them too general.

Teenagers seem to be more attracted to MMORPGs and CRPGs than adults. (Yes, most player are not teenagers, but (a) most of the real-life population is over 19, and (b) teenagers find MMORPGs and CRPGs more expensive than do adults. Despite these hurdles, almost 50% of the MMORPG-playing population is under 20.)

What "personal goals" are common to teenagers? Remembering back to those awful years (definitely not the best years of my life), I came up with the following list: (Feel free to make up your own.)

In case you haven't read between the lines by now, MMORPGs and CRPGs fit snugly into a stereotypical teenager's personal goals.

If a mass-market game were designed for adults, the question arises: What "personal goals" are common amongst adults? (Feel free to make up or modify the list to suit your opinions.)

Of course, these goals are drawn with very broad strokes. Everyone is different. For example: I like watching the international news, and I like science fiction and fantasy. From this, I've concluded that my fundamental "dreams" are as follows:

Mass market vs. niche

Notice that my goals don't particularly match what I've written for the standard "adult" goals. After all, everyone is different.

This difference affects a game's design: Teenagers are attracted to a game with virtual housing because it allows them to be an adult. Most/many adults would like to own their own virtual house so long as it's bigger/better than their current one. I'm not particularly attracted to virtual property, because in real life, I don't want anything larger/better than I already have.

Due to their production price tags, large worlds must target "typical" teenager or adult "dreams", like owning a big house or holidaying on a tropical island. Large Hollywood productions do the same.

Small worlds (and small movies) fulfil more niche dreams. I could take my personal goals and produce a world that fulfils them, but I wouldn't get many players. However, if my costs were low enough, a few thousand dedicated players might be all I needed.

Random comment at the end of the article

One way to create a sympathetic goal that I didn't think of when writing sympathetic goals is:

 

 


 

Storylines III

(A theory of massively single-player games)

(Back to TOC)

10 January 2006

by Mike Rozak

This article includes continuing thoughts from Storylines II. Of course, it's just one more thought experiment to add to the list; who knows if the idea will actually work.

Linear narrative, linear avatar games, and sandboxes

I am continually refining my thoughts about the difference between a linear narrative and game. For this article, I need to define them a bit more clearly than I have in the past, in order to make a point:

Of course, these three categories define a continuum, from linear to free-form.

Each category (narrative, linear, and sandbox) has strengths and weaknesses, which I've written below. Green items are strengths, red are weaknesses, and yellow are in between.

Linear narrative

Linear avatar game

Sandbox

Players/readers can read NPCs minds.

Novels and movies allow readers to "read" the characters' minds (especially in books), as well as see what the enemy is doing off-stage. Mystery novels (somewhat game-like) do not tell the player what the other characters are thinking.

Not only are game-NPC's minds fairly uninteresting to read, but the ability ruins the game aspects of the experience.

Not only are game-NPC's minds fairly uninteresting to read, but the ability ruins the game aspects of the experience.

NPCs behave in a realistic and believable manner.

Because authors can pre-script NPC dialogue and behaviours, NPCs can be very realistic.

AI is limited, so NPCs aren't very believable. Linearity allows some pre-scripting, so NPCs can be made to appear more intelligent than they really are.

Since there's no linearity, NPCs only have AI to rely upon, leaving them fairly dim. As a result, sandbox worlds often avoid NPCs, or associate NPCs with specific quests, which are more linear elements of the sandbox.

Both PCs and NPCs have a large variety of actions to choose from. (In Chris Crawford's terminology, this is the number of "verbs" available.)

Because everything is pre-scripted, any action that can be described verbally or visually is possible.

Only those actions which the developer programs in can happen. However, the linearity leads players down a certain path, and implies what actions players might "want" to take. Authors can (usually) predict the action and code for it, making the verb list appear larger than it actually is. Failure to predict a player's actions results in a "guess the verb" problem common to text adventure games.

Since players can and will want to do anything and everything, their choice of actions are ultimately limited. A typical sandbox game lets players walk/run, fight, craft, etc. If a player wanted to arbitrarily glue three matches together to form a triangle, they couldn't because the standard verbs wouldn't support it.

Eye candy quality.

Since everything in known in advance, eye candy is maximised. The overall quality of text in a novel is always superior to that of a text MUD, and the overall quality of graphics/sound in an animated movie is superior to that in in games.

Because of the linearity, special animations (or text passages) can be pre-written. If these special animations/text are long enough, they're called cut scenes.

All animations and text are stock, so they're (as a whole) not very good.

Foreshadowing and prescience.

Linear narratives frequently foreshadow what is to come in the story, using foreshadowing as a hook to keep the reader interested.

Linear narratives can use some foreshadowing, but only about events that they know the player will be forced to experience.

A free-form experience cannot foreshadow.

Serendipity (confluence of events).

Most linear narratives are designed so two characters just happen to meet at the right time, happen to have a hairpin to pick the lock, etc. Serendipity allows protagonists to get out of tight situations, as well as giving readers the desired sense of "things happen for a reason."

Serendipity can happen at certain points in the experience; a player can open a door to find an important scene just beginning. Of course, the scene's start was triggered by the door being opened.

The more a world is like a sand-box, the less likely that a player will be at the right place at the right time. Designers can "hack" in some serendipity by triggering scenes when players approach, but then the experience becomes slightly more linear.

Jump around time, as well as accelerating time through the boring bits.

Stories frequently jump forwards and backwards in time, as well as skipping over the boring bits with a few words.

Linear games can jump around time, but the player will probably be very confused. Furthermore, the time-jumps only serve to emphasise to the player the fact that they can't really alter the game's outcome.

Accelerating time through the boring bits is a common practice.

Jumping around time isn't possible.

Accelerating time is possible in a sandbox, but I haven't seen it used.

The experience can have a plot. That is, events that don't ultimately affect the path of the story (aka: the drive towards the ending) are ignored.

In a story, every narrated event serves to flesh out a character or to drive towards the ending. If an event doesn't, then it is removed from the book/script.

Because the overall flow of the experience is linear, it can have a plot. However, smaller events that are entirely controlled by the player may ultimately prove to be meaningless to the ending.

Plot? We don't need no stinking plot.

The player can be a specific character, as opposed to a general archetype.

Providing a specific character makes it easier to produce sympathetic goals and personal NPCs.

Stories can be about a specific protagonist.

Linear games can either specify the player's character ("You are Frodo Baggins".) or provide a more open-ended character ("You are a hobbit from the shire.")

While a sandbox could specify the player's character, doing so would push the experience towards a linear game.

Rewards for completing goals.

When a protagonist completes a goal (since the player cannot), the author has an enormous variety of rewards to offer.

Authors can offer a large variety of rewards, although not so varied as a story. Rewards not only include gold, loot, etc., but also changes to the world as a consequence of moving to a new pearl.

Rewards are usually limited to gold, loot, or character power.

However, in a multiplayer game, rewards for interaction are "handed out" by other players, and can be more varied, including social rewards.

The player's character can be placed in a specific scenario that is designed to be interesting. (Related to a "serendipity (confluence of events)", except in broader strokes.)

Frodo just happened to be the nephew of Bilbo Baggins, who happened to find the one ring a few years before Sauron was to attempt his return to power.

Same as with stories.

Specific scenarios are sometimes used, but don't work as well in a sandbox because there's no guarantee that the specially-manufactured scenario will come to fruition.

Problems are presented as interesting puzzles.

Not possible, except in mystery novels, which stretch linearity to the limit.

Puzzles are fairly common, especially in adventure games.

Puzzles are possible, but multiple-solution problems seem to work better.

Players can "do stuff", immersing the player.

One of the weaknesses of Choose Your Own Adventure books is that while they allow players to choose, they don't allow players to "do".

Stories do not allow the player to do anything (other than flip pages). They try to make the player feel as though they've done something by describing the protagonist's actions.

Players can do stuff.

Players can do stuff.

Players can make small choices.

Stories do not allow choices.

Players can choose where to walk, what to buy, exactly how to attack a monster, etc.

Players can choose where to walk, what to buy, exactly how to attack a monster, etc.

Players can make major choices, such as whether to be good or evil. Allowing players to make major choices both increases immersion, and customises the experience to the player.

Stories do not allow choices.

While players can make major choices, allowing them to do so do requires that the designer produce more branches in the larger linear story (turning it into a computerised Choose Your Own Adventure book).

Major choices are very easy to implement, and flow naturally from the sandbox's logic.

Customise the experience to suit the user.

Theoretically, a novel/movie presented on a computer could be customised to the viewer. For example: If the viewer were uninterested in romance, the romance scenes could be skipped or replaced with abridged editions.

Linear avatar games often customise the experience to the player. CRPGs allow players to chose their character's class and race, for example. Adventure games usually don't provide any customisation.

Customisation is common and expected in sandbox games. Users control the character's race, gender, class, equipment, etc.

Problems have multiple solutions.

Not possible.

Players are given problems to solve, but the problems typically only allow one or two solutions that the author has planned ahead of time. Consequently, many of the problems turn into puzzles.

Players can solve problems any way they chose, so long as the limited selection of verbs allows for the solution.

Players encounter "guess the verb" frustrations when they think they can do something but can't figure out how to tell the computer to do it, and/or the players could use verb X with object A, but not object B.

Not an issue because the player can't actually do anything.

"Guess the verb" frustrations frequently occur in linear games because linear games often rely on exceptional physics.

Since sandboxes can only use universal physics, guess-the-verb problems are not an issue.

Note: Sandboxes often include quests, which are small linear games, allowing them to take advantage of some features of linear games. Linear games often include cut-scenes, which are short linear narratives, allowing them to take advantage of some features of linear narratives.

Of course, you can modify this list however you see fit. The point of the table is that it shows that linear narration, linear avatar games, and sandboxes have their own strengths and weaknesses. Some players will prefer linear narration, others linear games, and others sandboxes. Individual preferences will also change over time. There is no "right" answer.

Ultimately, when designing a game, the designer needs to decide how linear the experience will be. A very linear experience will have certain strengths and attract one sort of player, while an open-ended sandbox will have other strengths and attract different players. (Note: People don't seem to like to watch totally linear experiences on their computers; they prefer TVs for that.)

Visualisation

In A tangle I tried to visualise what was happening in an avatar game. Here's another way to visualise what is happening...

In a linear narrative, the protagonist moves about and "does stuff", all the while time slowly advances. If you graph the protagonist's movement through space in X and Y, and time in Z, then what you get is a thin squiggly line that traces the character's movement (and actions) through time.

Now, forget what I said about X and Y being the protagonist's location in space, and Z being time. X, Y, and Z (and any other dimensions) merely represent the "location" of the character in arbitrary dimensions. Some of the dimensions might be locations, but many could be based on choices, such as how good or evil the character is. Even though the XYZ dimensions no longer have a specific meaning, the character's actions and "story" are still represented by a thin squiggly line in arbitrarily dimensional space.

Then, imagine the story becoming less linear until it turns into a linear avatar game. In the visualisation, the thin squiggly line thickens up, but still remains squiggly. The thickness of the line represents the number/frequency of "small" choices a player has. When the player is allowed to make major choices, the squiggling line branches, forming a tree; if the branches recombine then a rhizome is created. If you squint hard enough you'll even see the string or pearls formed by points where the player's small choices are narrowed into a thin line.

As the experience turns into a sandbox, players are presented with many more minor and major choices; The the squiggle thickens and produces abundant branches. It becomes so thick and so laden with branches that the squiggle becomes a complete tangle. This tangle is the visualisation of the sandbox, where players can do anything they wish.

Visualising storylines

If you have ever read a novel (which you probably have), then you've noticed that novels don't just provide the story of the protagonist. They also include other characters; Often, half the chapters of the novel will follow the story of other secondary characters. Some novels even include several protagonists.

If a thin squiggle represents one character's story, then a novel is a collection of squiggles. Because books and movies are inherently linear, an author will first narrate a bit of the protagonist's squiggle, then jump over to another character's squiggle and narrate a portion of that, then back to the protagonist's squiggle, etc.

By convention, novels only narrate characters' stories where the stories (a) are interesting to the reader, and (b) significantly and repeatedly affect the protagonist. For purposes of visualisation, if a character meets or somehow affects another character's story, then their squiggles touch. This means that a novel is a collection of thin squiggles (each representing a single character's story), all of which touch one another at one or more points.

Therefore, a linear avatar game is a collection of thick squiggles that occasionally touch. Most avatar games only follow the protagonist, so the game only includes one squiggle. However, some allow players to control different characters in different parts of the same instance of the world, resulting in the same basic structure as a novel (containing multiple squiggles, albeit thicker.)

Extending the idea to sandboxes produces several highly tangled squiggles that intersect one another. This tangle of tangles is almost impossible to visualise in detail. I don't think I've ever played a single-player sandbox game that had the player controlling several different characters in the same instance of the world. Single-player sandbox games do allow players to create several characters, but they each have their own world instance. Providing a sandbox with several characters (in the same instance) doesn't make much sense, since a sandbox provides so much flexibility that a single character is all the player needs to experience the game's instance.

However, I have played a multiplayer sandbox, where each player has their own character in the same world. A multiplayer sandbox is known as a world-like MMORPG.

A multiplayer linear avatar game is like a game-like MMORPG, but not quite. I haven't actually seen any multiplayer linear avatar games as I've described. I have seen game-like MMORPGs, such as World of Warcraft, that are part way between a linear avatar game and a sandbox. WoW isn't a pure linear avatar game because the storylines aren't well defined. Guild Wars comes closer, but it only has one storyline.

There are a few reasons why I suspect pure multiplayer linear avatar games don't exist at the moment:

1.         No one has seriously tried the idea. Most games are clones of other games with small evolutionary modifications.

2.         Existing game-like MMORPGs evolved from world-like MMORPGs, and haven't made a complete transition. As a rule, existing MMORPG players do not like linear games, since MMORPG players are attracted to world-like MMORPGs because of the other players, by the open ended gameplay (sandbox), and/or by the extended gameplay. A multiplayer linear game won't have the open-ended gameplay, nor will it take 500 hours to complete.

3.         It's a lot of work to make a game-like MMORPG with multiple storylines. Since storylines are inherently linear (with occasional branching), if there's only one storyline in a world then you and your closest 100,000 friends will experience the same storyline in the same world, making you feel like you're on an amusement park ride, not in a world. Therefore, a world must have many (4 to 20) storylines. This many storylines is not only a lot of work, but it is "wasted" content that most players won't ever see; Bean counters will strenuously object.

4.         "Multiplayer stories" don't make any sense since the experience is akin to sitting in a darkened movie theatre with thousands of other well-behaved people; they have no effect on the experience. As any die-hard sandboxer will tell you, multiplayer sandboxes make the most sense. A multiplayer linear game, often derided by sandboxers as a "massively single-player game", is some place in-between multiplayer stories, which are pointless, and multiplayer sandboxes, an obviously-successful application. Thus, it's unclear whether a multiplayer linear avatar game makes sense.

My thought experiments have revealed some reasons why they might work, even though players can't affect each other's larger "storylines". Player interaction can have smaller effects:

1.         Friends can help each other out on their storylines.

2.         Players can use the game world (and storylines) as a way to meet other people. See The dating game.

3.         Storylines can be designed to (occasionally?) require interaction with other players. Such interaction might include the buying and selling of items, teaming up with specialist player-characters from another storyline, or even some PvP. See The game loop.

Creating a multiplayer linear avatar game

Here are the challenges with multiplayer linear avatar games, as I see them:

1.         The world and its backstory must be rich enough to handle 4-20 storylines. The World of Warcraft's backstory explains why players want to go out and kill things, but not why a player would want to partake in a private eye or a town mayor storyline.

2.         The team must create enough content for 4-20 storylines. While this isn't nearly as much work as creating an equivalent number of single-player linear avatar games, it's probably 3x-10x as much content work. Don't forget that each storyline must include a few variations as well as a few major choices that require branching, requiring yet more content.

To reduce costs and to make the world feel populated, some/most content should be shared between at least two of the storylines. For example: The same crypt that attracts a vampire-slayer (storyline) might also attract an archaeologist (storyline).

3.         The 4-20 storylines must all be individually interesting, at least to a portion of the game-playing population. If only 1% of the players elect to play a storyline then it probably isn't worth including. The most popular storylines in MMORPGs are (1) kill things, (2) kill other player characters, (3) trade, (4) explore, and (5) forget about playing the game and just socialise. That leaves 15 storylines, some of which might include being a detective, Don Juan, military commander, ship's captain, or mayor of the town. Only ten storylines to go! (I suspect there is a connection between successful storylines and The dream factory.)

4.         As with a novel, players with different storylines will cross paths...

o    All the storylines must somehow interact with other storylines. An isolated storyline that includes no interaction with other players might as well be a single-player game. Likewise, if the storylines cluster into two disconnected subsets, then split the game into two or redesign the storylines.

For example: If explorers only ever interact with archaeologists, and archaeologists only ever interact with explorers, then either re-design explorers and archaeologists to interact with other storylines (like merchants), or get rid of both of them.

o    A specific interaction must make the game more fun for at least one of the players involved. Ideally, interactions will be fun for both players, but this won't always be the case.

o    Corollary: Interactions that are neutral fun-wise should be gotten rid of. There is no point forcing player interaction unless it will make the experience more fun.

o    Corollary: Players who like to play alone must be able to avoid interactions with other players. There might be a cost though, such as NPC merchants charging more than player merchants.

o    While players may not enjoy some of their player-with-player interactions, on the whole, player interactions should make the player's experience more fun. Any storyline where player interactions end up being a net minus for the player should be removed, since players of that storyline would find a single-person version to be more fun. The Player pyramid may have effect though; Theoretically, some storylines could have a net negative player-with-player interaction if the player were allowed to play for free.

o    Furthermore, any storyline which is a net negative to all other storylines should also be removed. However, if a player pay enough money and ends up subsidising other players then many sins can be forgiven.

Example: One player might choose the storyline of thief, while another might be a merchant.

At some point, the storyline of the thief might allow the thief to rob a player merchant (resulting in controlled PvP); obviously, the thief player will find the experience fun, while the merchant will dislike the experience. To counteract the merchant's negative experience with the thief player, the merchant will need a positive experience with another player, such as making a spectacular profit from another player, or perhaps having the pleasure of fingering the thief.

The thief storyline cannot be designed to cause grief every time the thief interacts with other players, since that would make the thief storyline a net negative. Consequently, some players will need to find encounters with the thief to be beneficial and pleasant, such as an archaeologist player that hires the thief to guide the archaeologist through a series of trapped tombs.

The thief is only allowed to rob other players when his storyline permits (which means only a couple of times). If the thief were given carte blanche, the storyline would not only be a net negative, but it would be an unknown negative, enabling a few particularly-successful thieves to ruin the experience for hundreds of players.

5.         At best, marketing will only be able to attract half of the current crop of MMORPG players. 50%-75% of them will intensely dislike the linearity and/or the relatively short gameplay of multiplayer linear avatar games. Consequently, marketing must convince players who like single-player linear avatar games that they'll like the multiplayer equivalent. This will prove difficult since most of these players will either dislike other players impinging upon their world and/or having to connect to the Internet while playing. They will also erroneously assume that all multiplayer games are time syncs and cost $15/month. See The law of new inventions.

 

 


 

Puzzles and problem solving

(Back to TOC)

26 January 2006

by Mike Rozak

I decided that I need to understand puzzles and problems solving more, since they significantly affect sub-games, which in turn, affect quest design. This article is the result...

To put it simply, every puzzle (or problem to solve) has some aspect(s) that makes it difficult or challenging to "solve". I was originally going to produce a list of puzzle categories, but realised that the categories can themselves be categorised into "what's the difficult part is", so I expanded my mission.

This article doesn't deal with the appearance/dressing of the puzzle, just what makes it challenging. Whether the puzzle is presented by a machine, NPC, riddle, or natural occurrence is irrelevant to this discussion.

My list of puzzle categories and the categories' categorisations follow:

I'm not entirely happy with these categories. They don't seem orthogonal enough to me. However, they're a start. I'll put this on the Internet and perhaps come back to it in a few months.

PS - While I wrote down many of these puzzle categories from my own gaming experience, some of them came from conversations, archived and contemporary, on rec.arts.int-fiction, listed in ifwiki.com.

 

 


 

Quest design 101

(Back to TOC)

26 January 2006

by Mike Rozak

Here are some thoughts about quest design, based on my own observations, and various books and articles that I've read.

The difficulty curve

Swords & Circuitry: A Designer's Guide to Computer Role-Playing Games, by Neal and Jana Hallford, points out that the "difficulty" of a CRPG should increase over time, but not in a nice smooth line. It should look more like the serrations of a saw-blade turned on an incline. The game starts out very easy, gets slightly more difficult, falls back to easy for awhile, gradually becomes more difficult, takes a step back, etc. By the end of the game, the final boss monster is the most challenging encounter.

There are some reasons for this difficulty curve:

I'd like to point out one more thing about the difficulty curve; there are actually two difficulty curves. One is the curve faced by players that are inexperienced at CRPGs (or otherwise unskilled/inept), and the other is a lower but parallel curve that skilled (hard core) players experience. The lower curve (for skilled play) needs to exist because it occurs naturally when the game rewards intelligent actions. Basically, players who think, learn from the game, and use their own skills/intelligence do better at the game. If both curves were the same, then a player's personal skills and insights wouldn't impact the player's progress. Players would realise this and feel cheated, like older children feel when they realise there's no skill involved in Chutes/Snakes and Ladders.

Unfortunately, this means that hard core gamers, who are looking for a challenge, find any given CRPG to be easier than casual players do, and less challenging. The difficultly level cannot be universally increased, since that would make the game unplayable to casual gamers. It is possible to create a "difficulty level setting" that players can select, and most CRPGs provide for this. An increasing difficulty curve also mitigates this problem, but it means that casual players won't have the skill to finish the game.

Adventure games have a larger separation in the difficulty levels experienced by casual and hard-core players because all the skill comes from the players, not the player characters. Due to their nature, adventure games can't effectively implement a difficulty-level setting. This means that a given adventure game is almost always targeted at either hard core or casual gamers, not both. Many casual gamers will "cheat" by downloading a game walkthrough for the puzzles they can't solve.

Sub-games throughout the game

Here are some rules about sub-games:

Sub-games in quests

As I've discussed elsewhere, a quest is a sequence of sub-games wrapped within a "story". CRPGs tend to rely heavily on the combat sub-game, but many more sub-games are possible.

Sub-games should be carefully placed in quests:

Stories

Some additional thoughts about "stories":

Choices

Players must be given choices. As I previously mentioned:

Consequences

All quests result in consequences to the player, player character, and/or the game world.

Some sample consequences are:

If possible, players should be given a choice of consequences. There are several ways to do this:

String of pearls and threaded storylines

Due to technical limitations, games are usually designed to be a string of pearls. Basically, a player enters a section of the world where they have a large number of choices (and quests to choose from), but at some point their choice in quests narrows down to the point where the player has only one quest left. Once they complete the quest, they move on to the next pearl in the string.

In a CRPG, the transitional quest usually involves killing a boss monster for the pearl (such as the bandit king working for the evil overlord). In adventure games, the transitional quest unlocks a door that lets the player into a new section of the world. In both cases, the consequence of the last quest in the pearl is to allow the player into the next pearl.

Unfortunately, by it's very nature, the "pearl" narrows down into a string, at which point the player is choice-less. You must compensate for this lack of choice:

Memes

Finally, try to include something memorable in each quest. Without memorable events, players won't recommend the game to other players.

 

 


 

Choices III

(Back to TOC)

1 March 2006

by Mike Rozak

This article is a continuation/summary of the "laws" of choices that I've been writing down in Choices, Choices II, and Choice and consequences.

Available options within a choice:

Strong and weak choices:

Consequences:

Miscellaneous:

 

 


 

Word of mouth

(Back to TOC)

1 March 2006

by Mike Rozak

According to Richard Bartle in Designing Virtual Worlds, MUDs with the longest lifespan seem to cater to all player types: achievers, explorers, killers, and socialisers. MUDs that attract only specific player types (such as only killers) quickly die out.

Recently, I've been trying to design a setting for my virtual world. One setting that interests me, both from a development POV, and as a hypothetical player, is a "realistic" alien environment, like the TV shows, "The Future is Wild" or "Alien Planet". For those of you that haven't seen these shows, they use computer graphics to imagine what Earth's animal life might evolve into 25 million years from now, or what animal life might be like on an alien planet.

Although neither show includes sentient life, if they did, the aliens certainly wouldn't look like humans with pointy ears. Intelligent alien life might look more bizarre, like land-dwelling octopi, for example.

The problem with a realistic alien setting is:

1.         Most people can barely relate to pointy-eared humans in Star Trek. Very few can relate to intelligent octopi.

2.         A completely alien planet with intelligent octopi characters screams out "explorer", drowning out other possible storylines. Some storylines, such as a romance, are impossible; it's hard to be romantic with an intelligent octopi.

The question arises: If I were to create a realistic alien world that is only attractive to explorers, how many players would I end up with? According to Richard Bartle's observations, the answer is, "Not many."

A broader model

Richard Bartle explains why a world needs to contain a mix of achievers, explorers, killers and socialisers. Basically, socialisers benefit from achievers because achievers give socialisers something to talk about. Achievers benefit from explorers, who explain the finer points of the world. Killers like having socialisers around as prey. Explorers like having achievers around that they can slowly release pearls of wisdom to them.

Previously, I tried to come up with a broader models in Ecology of a MMORPG and Intertwined relationships.

However, I think I can go a bit further now:

1.         As I discussed in Storylines II and Storylines III, you can think of a virtual world as a collection of single player (linear) games where (a) the games take place in the same world, and (b) the games and players interact with one another. (I'm not saying that virtual worlds are limited to a combination of linear storylines, however.)

Current MMORPGs rely upon the following games/storylines: Kill monsters for a living, kill player characters for a living, craft and trade for a living.

2.         Although individuals jump from one storyline to another during gameplay (such as crafting to combat), players tend to stick with their favourite storyline/game. This means that a player personality type (not necessarily Bartle's four/eight types) is associated with each storyline.

The same general rule holds for movies, books, and television: Individuals reliably like a few genres (types of storylines) and avoid the rest. Personally, I like science fiction and fantasy, but dislike romance. I have friends who are the opposite.

3.         In a virtual world, storylines interact with one another, sometimes positively, often negatively.

In MMORPG terms, PvP doesn't work well with scripted world-building (like Second Life) because PvP players use the infinitely-powerful building-scripts as a weapon; it's like giving every single PvP player their own arsenal of nuclear weapons to use... and results in complete and utter devastation.

PvP combat and crafting/trade seem to work well when combined into one game, however.

To switch media to movies, "romantic comedies" are a combination of two genres/storylines, romance and comedy. Romantic comedies do well at the box office. "Romantic horror films", to combine two different genres, do not do well. (Has there ever been a romantic horror film?)

According to this generalisation (which I don't think is quite correct), a virtual world should include multiple storylines (which end up attracting different player types) only so long as the interactions between the storylines are a net positive. This implies that my alien world idea might work. An alien world would obviously attract explorers, players that like to kill alien monsters, and players that like to trade with aliens for bizarre goods. Players interested in romance would go elsewhere, but that's no great loss.

Word of mouth

Despite my model's reassurances, I don't think my alien world would work, and here's why...

Players of virtual worlds actively recruit their friends to play because the game is more fun when one's friends are involved. I suspect that friend-networking is the single largest marketing force that virtual worlds have. Advertising and fansites fall a distant second and third.

This relationship means that marketing (and design) of virtual worlds is less like a TV, books, or single-player games, which are all solitary experiences, and more like the marketing (and design) of a movies and restaurants. People go to movies and restaurants with friends; I may really like a Chinese restaurant's Mongolian beef, but if my friends don't like Chinese food, we won't visit my favourite restaurant. Instead, we'll choose a restaurant that everyone can enjoy (or at least not dislike).

As far as my alien world goes, I may like it, but my friends almost certainly won't; It's way too weird for them. They'll want to play something different. I'll be in the minority and be forced to follow them wherever they go, such as World of Warcraft.

Categorise players based upon what form of entertainment they consider "fun", using whatever metrics you like. (This might be Richard Bartle's player types, or something different). It seems to me that friendship is only weakly correlated to player type. In restaurant terms (and to state the obvious), friendship is only weakly correlated to restaurant preference; I don't choose my friends based upon what sorts of food they eat. The inevitable result is that most restaurants are "family style" restaurants that offer a varied menu. Likewise, most virtual worlds must cater to most player types.

The "cater to all player types" rule is only strengthened by the realisation that friends will bring their friends into the virtual world too. The friends of my friends have play styles that are completely unrelated to my own.

Consequently, if my realistic alien VW doesn't cater to players interested in romance, I'll not only lose romance-liking players, but I'll lose any player who has friends that like romance. The effect on world popularity is almost exponential! If 1% of all players would ardently like to play in an alien word, and if 10% of all players would only play if they were cajoled into playing by the rabid 1%, then the probability of getting four friends to play, one of whom strongly wants to play, and three of whom are in the 10% group, is 0.001%. That's an awfully small market.

Revisiting the restaurant analogy: I guesstimate that 5%-10% of Australians are vegetarians. The percentage of vegetarian restaurants, however, is certainly less than 1%.

Exceptions

I can enumerate some exceptions to my rule of "Thou must cater to all player types and build the equivalent of a family restaurant":

1.         Age grouping - The friends of most teenagers are teenagers. The friends of most adults are adults. The friends of most retirees are retirees. Therefore, you can produce a virtual world targeted solely at teenagers, adults, or retirees.

2.         Age-specific storylines/genres - Teen sex flicks only appeal to teenagers. Undoubtedly, some storylines/games will have similar age resonances.

3.         Education - In the US, most friends of college-educated adults are also college educated. Most friends of high-school educated adults are also high-school educated.

4.         Wealth - Education and wealth are correlated. Furthermore, wealthy people tend to spend most of their time with other wealthy people, because of the exclusive neighbourhoods where they live, their job, and the awkwardness of showing their less wealthy friends their latest $100K sports car.

5.         Language (and culture) - Most people's friends are from the same country (aka: language and culture).

6.         Segregated cultures - Many cultures are segregated based on gender, race, and/or religion. Such segregation could be mirrored in virtual worlds targeted at the culture.

7.         Short virtual world experience - The shorter the virtual world experience, the less strongly this rule holds because friends are more willing to put up with an experience they really don't like.

For example: If a friend wants to see a romance movie, I will go (with protests). However, if that romance movie were ten hours long, I would suddenly find an illness/excuse to avoid the movie; Ten hours of fantasy, such as The Lord of the Rings, and I'm feeling fine with an open appointment book.

8.         Niche markets - If a player wants to experience an alien world so badly that he's willing to forgo his real-life friends (or if a player doesn't have any real-life friends), then the player will play alone and (hopefully) meet new friends in the virtual world. See The dating game. This observation implies that my alien world would attract somewhere between 0.001% (if players insist on hanging out with real-life friends) and 1% (if players abandon real-life friends) of all players.

Another issue I need consider is this: Do I want to be a small fish in an ocean, or a big fish in a small pond? So many family restaurants serve the (admittedly large) market, that individual family restaurants aren't very profitable. Being the only one in town, a niche-market vegetarian cafe may do well despite its small customer base.

Effects on world design

The requirement to cater to all player types affects world design:

1.         A family-restaurant world must be compartmentalised by danger level, weirdness/explorer appeal, player-vs.-player, etc. While a single player game, such as a horror survival game, can be universally dangerous and weird, a virtual world must contain a bit of everything. Players interested in romance will probably want a safe (non-combat) environment without anything too weird (no octopi aliens). Explorers will want the aliens, and a bit of danger. Crafters and traders won't mind the aliens, but they won't want any danger.

2.         Whatever the storyline, a family-restaurant world can't change too much throughout the storyline.

This one is a bit tricky to explain... To be satisfying, storylines require that world changes based on the player's actions. Change in multiplayer games requires fractured reality. Unfortunately, what is a "satisfying change" in storyline A, is not the same as the change required for storyline B. Different changes require different fractures, potentially producing a world with so many fractures that players from different storylines will never meet.

Example: If all players were on the same storyline of killing the evil overlord then at every milestone (such as killing an important minion of the overlord) the player could be moved a new section/fracture of the world. GuildWars does this with pre/post apocalypse fractures. The evil overlord storyline might have three fractures: one before the evil overlord gained power, one during the evil overlord's tyrannical reign, and the last taking place during the revolution. This fracturing wouldn't work with multiple storylines (a consequence of catering to multiple player types) because players interested in trade (and not interested in slaying the overlord) wouldn't care which fracture they're in. Nor would they appreciate being randomly moved from one evil-overlord fracture to another. So, which one do they get placed in? Whichever one is used, two thirds of the time, players from the evil-overlord storyline won't be able to interact with players from the trade storyline.

3.         A niche virtual world must provide an easy and effective way to meet other players. See The dating game.

Why I was vague about player types

I've been purposely vague about what the different "player types" are because (a) it's not necessary for the argument, and (b) locking into specific player types limits one's perception of possibilities.

Richard Bartle posits four basic player types (extended to eight in his book):

Some "player types" of novel readers (aka: genres) are:

Some player types (genres) from movies and TV:

Player types (genres) from games:

An evolutionary explanation for entertainment has some other player types to add.

I listed all these player types (genres) because they're all valid depending upon how you look at the issue. The lengthy list also illustrates that people's ideas of "what's entertaining" varies greatly.

The same can be said for food: Family restaurants usually offer 50+ meals, not only including traditional American dishes, but also genericized fare from "niche" restaurants, "Mexican" taco salads, Indian curries, Chinese spring rolls, and a few vegetarian dishes. Likewise, a virtual world that doesn't explicitly target a niche market must offer an equally large menu of experiences, catering to the enormous variety of entertainments that players find entertaining.

More ramifications

At the moment, most MMORPGs only target a tiny fraction of the player types listed here. Even with a scope limited to Bartle's player types, most MMORPGs only cater to achievers and killers. According to my thought experiment, expanding a world's design so that it caters to even one or two more player types (whatever your player-type model) should significantly improve the MMORPG's appeal... assuming it's trying to be mass market.

 

 


 

Levels, friends, and lobbies

(Back to TOC)

9 March 2006

by Mike Rozak

A month ago, Raph Koster's blog discussed how levels make it difficult for friends to play together. That got me thinking, and when combined with ideas from The parlour, the lobby, and the sandbox, I came up with some interesting thoughts.

The problem

Levels are a useful gameplay device. They provide the following "features":

Unfortunately, levels have some major downsides with respect to friends:

Of course, some work-arounds to the level/friends conflict have been invented:

All of these work arounds help, but they don't entirely solve the problem.

Levels have additional problems, namely that players feel railroaded into one advancement path, and don't feel like they can individualise their characters. To solve the railroading problem, some virtual worlds use skills.

Skills make the "friend" problem worse!

To allow players to better customise their characters, many virtual worlds let players advance individual skills rather than levels. If all of the skills are from the same combat oriented skill-family (swordsmanship, archery, dodge, etc.), then the sum of a character's skill levels is a good measure of how "combat worthy" the character is. This combat worthiness can be used to determine what content the character can handle, and what player characters it can group with. Basically, "combat worthiness" is another name for "level". The only difference between individual combat skills and levels is that the individual skills allow some customization, giving players more freedom.

However, the logical next step with a skill system is to introduce skills like "basket weaving". This presents a problem!

Imagine that the game has two skills (or skill families), "combat worthiness" and "basket weaving".

1.         A character with combat worthiness of 20 can play in dungeons and quests targeted at combat worthiness levels 15 to 25. This behaves just like levels, where a player can partake in 10% of the content at any point in time.

2.         Likewise, a character with basket weaving skill 30 can partake in dungeons and quests targeted at basket weaving levels 25 to 35. Again, it's just like levels.

3.         What happens if a quest is targeted at players with combat worthiness of 20 AND basket weaving of 30? What percentage of players will be able to participate? 10% times 10% = 1%!

Consequently, if a world has two orthogonal skills (or skill families) as opposed to a single orthogonal skill (or skill family) then:

As with levels, some work arounds exist:

No skills or levels

A third alternative is to not have skills and levels (or to have very weak ones), making all characters equal. This trivial solution always lets friends play together, but it destroys some of the fundamental tenants of CRPGs, levels (as above) and classes. Classes are important because they allow players to tackle problems using character-skills that suit the players, customising the gameplay experience to the player.

One way to handle not having skills or levels is described in Storylines III.

My intuition tells me that if there are no levels/skills for players to improve (goals), and there are no classes (customization), and there are no storylines (goals), then the world won't be very fun for most players. This might be part of the reason why Uru Live failed.

Lobbies and friends

If a game designer wants to ensure that friends can play together, and/or that players can meet and play with strangers, another solution exists, the lobby.

Basically, a virtual world designed around a lobby has the following features:

1.         There is a main world which acts as a lobby. Players can't do much in the lobby besides meet up with other players. Players have characters in the lobby, but the characters are basically holograms without substance.

2.         The lobby includes a "dating" services that helps players meet. It maintains a record of what content (instances) the player hasn't yet experienced so that players won't be grouped unless they have content that they might wish to experience together. The lobby might also link up players based on age, how much time they have to play in the session, or other search criteria.

3.         An instance is a private world that only a group of friendly players can enter. It is created when a group of players meets in the lobby and decides to enter the instance. The instance is destroyed when the players leave.

4.         Instances are short enough (1-3 hours) to be completed in one session. An instance could potentially be saved mid-way, but this would require that the same group of players get together at the same time on another night, something that is logistically difficult, even among friends. It is even more difficult for casual players who only play five hours a week.

5.         In games like GuildWars and Dungeons and Dragons Online, players bring persistent characters into the instances, which doesn't solve the "playing with friends" problem. In the lobby that I'm describing, players are given instance-appropriate characters when they enter the instance. Thus, if they enter a standard dungeon, players are given fighters, magic users, clerics, and thieves. If they enter a murder mystery, they're given detective characters. These player characters' backgrounds might even be specifically designed to fit in with the instance's story.

6.         Some instances are sequels to previous instances, just as television series are discrete episodes, yet form a longer narrative. Players can skip around "episodes" of an instance series in order to play with friends.

First, I'll list some of the downsides of a lobby world:

However, lobbies have many benefits:

 

 


 

Oblivion: Full spectrum content, from hand-generated to procedural

(Back to TOC)

2 April 2006

Revised 4 April 2006

by Mike Rozak

Several months back I played Fable: The Lost Chapters, and wrote up the aspects of Fable that I thought were well designed and unique. I just finished playing through part of The Elder Scrolls IV: Oblivion and thought I'd do the same. This is not a review, but an analysis of what works from a design perspective.

Hand-generated vs. procedural content

Ever since Will Wright demoed Spore, developers have been all hyped up about procedural content, claiming that it will save the world (along with Java, multimedia, and pet rocks). While I use procedural content in my own game, I do so with the understanding that procedural content has limitations. Most importantly, procedural content can quickly become repetitive and boring.

Oblivion uses procedural content, but doesn't rely solely upon it. This is where Oblivion excels: Oblivion has a great balance of procedural content, hand-generated content, and in-between procedurally-aided content.

Some definitions follow:

Realistically, there's a whole spectrum of hand-generation vs. procedural content: (This spectrum metaphor is important for later on.)

Hand-generated

Procedurally

assisted

Procedural

How Oblivion handles hand-generated and procedural content

Oblivion has a mix of hand-generated, procedurally assisted, and procedural content. Importantly, it has a well-balanced mix of content.

Here is a brief description about how Oblivion's world is generated (given my understanding and perception of it):

Procedurally assisted

The world's topography is roughly painted by an author. Procedural algorithms, such as rainfall erosion, are applied to the author's topography, producing gullies and streams.

Hand-generated

The forest type (evergreen, deciduous, grasslands, etc.) is painted on the topography by the author.

Procedural

Individual trees, shrubs, and grasses are procedurally placed at run-time.

Hand-generated

Monster encounter sites are placed by hand, but in a somewhat random pattern. Monsters tend to be near dungeon entrances and roads, but there is no compelling reason for the monster to be in that exact location.

For example: Many bandit camps are placed in valleys near the road. Realistically, a bandit camp would be on a sheltered hill near the road so the bandits could climb the trees and overlook the road. Or, the camp would be in the valley near a hill, but the bandits would spend all their time on the hill.

Procedural

The specific monster that appears at a site is determined by the player's level, although the author does choose the type of monster (goblinoid vs. demonic vs. animal).

Hand-generated

Dungeon entrances are hand placed, but their appearance is based on a stock set of entrances. Dungeons are evenly scattered around the world, but little attention seems to be paid to the reasons why dungeon X is in location Y.

Procedural

Some dungeons, such as the Oblivion gates, are randomly placed throughout the world.

Procedurally assisted

Dungeons are created by hand using a set of "tiles". The dungeon layouts appear haphazard to me, and not as carefully laid out as they could be. I suspect the haphazard layouts come from a combination of tile limitations and because the designers don't have enough time to perfect the layouts.

To contrast Oblivion's layouts with another CRPG: Neverwinter Nights' dungeons have specific store rooms for food (for the monsters), design elements that Oblivion lacks. Many of Oblivion's dungeons don't even have a boss monster or other obvious end-point.

Hand-generated

Dungeon monsters and loot are placed by hand, but somewhat haphazardly.

Procedural

The specific monster and loot that appears at a site is determined by the player's level, although the author does choose the type of monster (goblinoid vs. demonic vs. undead).

Hand-generated

Cities are placed by hand, and their external appearance (always walled to reduce polygon counts) is custom to the city.

Hand-generated

City streets and buildings are placed by hand, using predefined building models that are repeated though-out the city.

Procedurally assisted

Most NPCs are placed by hand, although guards are more procedural. NPC appearance is hand-selected using sliders that modify a basic model.

Hand-generated

NPC actions are controlled by AI. The NPC's AI goals are chosen by the author.

Procedural

The minute-to-minute details of what the NPC does is left up to the AI.

Hand-generated

Quests are hand-generated, and reasonably detailed and creative. (Quests in WoW and EQII, are hand-generated and not as creative, with more of yellow/green colour because they could almost be procedurally generated.)

Hand-generated

The main quest, returning an emperor's son to power, is tightly controlled and very detailed.

The colour spectrum applied to NPCs

Here's example of the spectrum with regards to NPCs:

Hand-generated

These are high-quality NPCs that the player will meet again and again throughout the game. The player is supposed to develop strong emotions about the NPC.

In Oblivion, the major NPCs appear in the main quest, such as the Emperor's illegitimate son, Martin. Martin is a priest with a chequered past (which I haven't discovered yet) that doesn't know he is royalty. He's uncertain how to handle his new role as emperor... basically, someone the player is going to like.

In the Myst series, almost all the characters are hand-generated "red". Of course, the number of characters in each Myst game can be counted on one hand.

Hand-generated

Orange NPCs are supposed to leave an emotional mark, but will only be encountered once or twice.

In one mage's guild chapter in Oblivion, the head of the chapter asks the player to find a guild member who has "disappeared". Meanwhile, the chapter's head continues to practice low-level magic like summoning imps. Talking to the other members of the guild reveals that they don't respect their current chapter leader because she's not skilled enough. It turns out that the wizard who has gone missing turned himself invisible as a joke, knowing that the leader was incapable of casting the appropriate spells to find him and then turn him visible.

Procedurally assisted

This NPC is the standard MMORPG NPC. He has a name and an uninteresting quest to hand out, like "Bring me some butter beer and I'll give you 10 gold." While the player knows that the NPC likes butter beer, there's not much personality there.

It wouldn't be difficult to randomly assign every NPC a favourite food/beverage and automatically hand out similar quests. There's very little work for an author to do.

Procedurally assisted

This NPC only has a name and a job. He is nothing more than a human-looking vending machine.

Procedural

Blue NPCs have names, wander around, and might deliver a rumour. When killed, they are automatically respawned.

Procedural

Violet NPCs don't even have a name. They have no purpose except as scenery (much like procedural trees) or monsters to be killed. When killed, they are automatically respawned.

A world with only red and orange NPCs sounds enticing until you realise that due to production costs, the world would only have a handful of NPCs. Ultimately, this unpopulated world feels very empty. Myst is a perfect example.

A world with only yellow and green NPCs has many more NPCs because they're cheaper to implement. It feels populated, although still a bit sparse. The NPCs are fairly uninteresting though. Most MMORPG cities resemble this.

If a world were only populated by blue and purple NPCs then there would be a hoards of them, but they'd be universally pointless. MMORPG wilderness and dungeons are typically populated by blue/violet monsters.

Oblivion has NPCs from all colours of the spectrum, although it would be nice to see more blue and violet NPCs wandering around the cities so the cities looked busy.

Mixing the colours of the spectrum produces white

If you look at the colours I've use to label Oblivion's content, you'll notice that they come from every colour of the spectrum. This makes Oblivion's combined colour, white-ish.

If I were to create the same table for any of the Myst games, the colours would be almost all red. Diablo and Rogue would be almost entirely blue or violet. Most MMORPGs rely heavily on yellow and green.

The advantage of being white is that it's a bit of everything. My perception of Oblivion (so far) is that it's a huge world (a result of procedurally generated content) with splashes of meaning/plot interspersed (from the hand-generated content). Myst feels less like a world because it's so small (which is one reason Myst takes place on an island). MMORPGs feel large, but they are devoid of meaning (because there isn't much red hand-generated content).

Notice that MMORPG NPCs are chromatically between finely hand-crafted and procedural NPCs. However, being in the middle (yellow or green) is not the same is being white. MMORPG cities are places to go and shop ,or to get quests from the NPC vending machines. They don't contain NPCs that players will care about, nor are there random NPCs wandering around to make the city look busy.

White is important. In my opinion, Oblivion is a better game than any of the Myst series or any MMORPG I've tried, mainly because it is "well rounded"... aka: It has all the colours of the spectrum. I suspect that games which manage to include a range of content, from hand-generated to procedural, will ultimately be better games.

To use a medieval analogy appropriate to Oblivion, a quality sword must be forged with both hard and soft steel. A blade entirely of hard steel will be very sharp but will easily break. A blade entirely of soft steel won't break, but it will not cut well. A compromise of a sword that's uniformly medium-hardness isn't a terribly good weapon either. The proper combination of hard and soft steel is critical.

Another way of looking at this

When developing an avatar game, you have the option of spending money on content creators (hand-generated content), programmers (procedural content), and/or procedurally-assisted content (content creation tools that make content creation a breeze, involving both programmers and content creators.)

As per my earlier discussion, the "content" produced by content creators feels and behaves differently than the "content" produced by procedural content or procedurally-assisted content.

Individual players will prefer the different types of content based on their individual personalities and their current mood. For example:

Contrast this to a game like Myst, where if I don't want to partake in the main plot, my only option is to stop playing. Or a MMORPG, where there is no larger story worth mentioning, leaving the experience of killing hoards of orcs somewhat meaningless.

p.s. - One major gripe about Oblivion

Morrowind, Oblivion's predecessor, had one major problem: Given that your character was level N, many of the dungeons you'd wander into would either be too tough (designed for level N+5 or higher), or too easy (level N-5 or lower). Consequently, most dungeons would require a speedy exit because the dungeon was way too difficult, or the dungeon was a piece of cake. Consequently, there was rarely any challenge; most dungeons were either too hard to even contemplate, or way too easy.

Oblivion fixed this problem, but with an equally bad solution. (I admit that there might not be a good solution.) Almost all dungeons automatically adjust their difficulty level to suit the player's character level. This is good in that all dungeons are now challenging, but...

 

 


 

Emotions

(Back to TOC)

29 April 2006

by Mike Rozak

Discuss on www.mXac.net/forums

A few years ago, I read Creating Emotion in Games, by David Freeman. One of the points that David Freeman made (or at least implied) is that games should be designed to evoke a variety emotions from players, like an emotional roller-coaster.

I've been a bit sceptical about this idea. However, lately I've been toying with the idea that if you broaden the commonly accepted understanding of emotion, emotions (in their new sense) could explain why people play games. Interestingly, the list of broader emotions also includes some concepts from ludology, where researchers have defined a number of "types of fun", such as "hard fun". Many of the ideas also tie into my thoughts about evolutionary fun.

Warning: These ideas are half baked. I'm just wandering down a path and seeing where it takes me.

Some emotions

How have I expanded my definition of emotions?

To begin with, I'll enumerate some "animalistic emotions" that are common to most mammals:

Some emotions that humans experience are more primate specific, although they do occasionally appear in other animals:

Finally, I'll broaden the commonly accepted list emotions to include some "emotions" that are specific to humans:

Notice how my human-only emotions aren't usually called emotions. They're often prefixed by "sense of" or "feeling of" though. They act like emotions since they're rewards (or punishments) handed out by the brain, and people go out of their way to cause these "emotions".

Table of entertainments and emotions they can evoke

With the expanded definition of emotions, I noticed that every form of entertainment evokes some of these emotions. Not surpassingly, some forms of entertainment are better at evoking some emotions than others. Below is a list of emotions along with capital X's to indicate if the entertainment is very good at producing the emotion, and small x's if the entrainment is less adept at evoking the emotion.

Emotion

Stories

Sports (watching)

Single-player computer games

Sports (playing)

Multiplayer computer games

Anger

x

X

X

Dominance

x

x

X

X

Excitement (adrenalin)

x (action movies)

x

X

X

X

Fear

X

x

x

X

Frustration

x

X

X

X

Hate

x

X

X

Lonely

x

x

Lust

X

x

Surprise

X

X

X

X

X

Anticipation

x

X

X

Boredom

X

X

X

X

X

Disgust

X

x

(?)

Feeling of comradery

x

X

X

Jealousy

x

X

Love

x

(?)

Sadness

X

(?)

Shame/guilt

x

X

X

Epiphany

x (mysteries)

X (adventure)

(?)

Laugher

X

Sense of being needed

x (CRPG)

X

X

Sense of achievement

X

X

X

Wonder

x (fantasy, sci-fi)

X (adventure, CRPG)

(?)

The (?) in multiplayer computer games indicates that it's possible, but not catered to in current MMORPGs.

Some interesting revelations...

This table produces some interesting revelations:

 

 


 

Gates and keys

(Back to TOC)

29 April 2006

by Mike Rozak

Discuss on www.mXac.net/forums

Avatar games segment their worlds so that players can't enter portions of the world without completing specific tasks. Designers do this to ensure that new geography content is gradually opened the user throughout the life of the game, preventing the user from experiencing all the geography content at the game's outset, thereby weakening the experience. (Character abilities, such as spells, are also gradually unlocked throughout the game.)

I just thought I'd spend some time listing different styles of gates and keys. First, the most common mechanisms:

Some solutions that restrict world access that aren't exactly gate-and-key:

 

 


 

The NPC-conversation wall

(Back to TOC)

29 August 2006

by Mike Rozak

Discuss on www.mXac.net/forums

I am a mouse running betwixt the feet of elephants. Consequently, I spend inordinate amounts of time figuring out where the elephants will roam.

The elephants (major MMORPGs)

Here is my best guess as to what the major ($20M+) MMORPGs will "innovate" over the next five years:

In case you haven't noticed, I'll point out some larger trends:

Elephant wannabes (minor MMORPGs)

Since I am merely a mouse, I also need to watch out for the minor MMORPGs (budgets less than $20M). Here's what I think they'll be up to:

The NPC-conversation wall

You may have noticed that I failed to include "more intelligent NPCs" in either the major or minor MMORPG list.

There are two kinds of "intelligence", neither of which will be used in MMORPGs (or so I think):

NPCs with personality, ones that you could actually care about, present a number of technical and design problems that form a sizeable barrier that won't be quickly overcome, the NPC-conversation wall.

The "NPC-conversation wall" consists of the following elephant-blocking stones:

1.         For the most part, players aren't requesting conversational NPCs - They ask for more weapons, more monsters, better graphics, ship-to-ship combat, etc. They either don't think that conversational NPCs are possible, or don't want them (perhaps because the game-player demographic is self-selected to not care about such things).

2.         Intelligent NPCs require much more server-side CPU - A factor of 10x the server-side CPU currently devoted by MMORPGs is easy to imagine. 100x is also possible.

3.         Intelligent NPCs require enormously more server-side memory. After all, a NPC won't be realistic if it forgets that it talked to you just yesterday. Just consider of the numbers: An average MMORPG server has 3000 concurrent peak users, which implies 15,000 players, each with 4 characters each, or 60,000 characters. 60,000 characters times 1000 NPCs is 60,000,000 relationships to keep track of! Even if most relationships are only tens-of-bytes of data, many will be several kilobytes.

4.         Player density must be low - A typical MMORPG NPC can "talk to" a dozen players at once since the NPCs don't really talk; they just display private dialog boxes on each player's screen. A realistic and believable NPC wouldn't be able to do this, and would only be able to talk to one, maybe two, players at a time. This either requires a much lower player density, or many more NPCs, such as several weapons merchants in the same area.

5.         Text-to-speech - MMORPGs are transitioning from text-based NPC dialogues to recorded speech; recorded speech makes for better eye candy than text, as well as working synergistically with 3D visuals. Unfortunately, recorded speech severely constrains the ability for NPCs to carry on a dialogue because its impossible to record every single utterance that might come out of a NPC's mouth, especially when multiplied by the number of NPCs.

The only solution to this problem is text-to-speech. Unfortunately, text-to-speech sounds lousy and will scare away most players. It sounds even more lousy when intermixed with recorded speech. Any MMORPG that already uses recorded speech (which will soon be almost all of the major MMORPGs) will find it (nearly) impossible to transition to text-to-speech.

6.         Typed responses or speech recognition - No matter how socially intelligent a NPC is, if a player's responses are limited to a menu of four phrases, the NPC will only have four possible responses, nullifying any advances in intelligence.

There are two solutions: Either allow the player to type in a response and use natural-language understanding to interpret what the player said, or use speech recognition to transcribe the player's speech before passing it onto the natural-language understanding system.

Unfortunately, current natural-language understanding requires that players forgive it's limitations; players can't just say anything they like, much as text-adventure players continually run into the "guess the verb" problem.

Nor do players like to type (or even know how). Sadly, typing is the only option since speech recognition doesn't work well enough yet.

7.         Large number of NPC animations - In games such as Oblivion, where NPCs are smarter than your average MMORPG NPC, you see another problem: Characters have a limited palette of animations that they can perform, mostly involving movement and combat. This limits how much personality can be expressed by the NPCs; Theoretically, one NPC might swagger, while another might daintily sip her tea. Standard MMORPG animations don't include "tea sipping" or "swaggering", let alone "dainty tea sipping". Needless to say, NPC animations are very expensive to produce.

8.         Faces - Faces are critically important for realistic NPCs. Unfortunately, the following problems arise:

o    MMORPG UI needs to be redesigned to that NPC faces are more than just a few pixels high. They actually need to occupy most of the screen.

o    As soon as faces are enlarged, players will realise that the faces all look the same. To solve this, MMORPGs will need more complicated and varied facial geometry, textures, hair, jewellery, clothing, etc. This means higher development costs and more memory on graphics cards.

o    Attempts at procedural face animation, as in Oblivion, result in characters whose facial animations are so "wrong" that the characters look mentally ill.

9.         Cut scenes - One important way to express a NPC's personality is to show how they interact with other NPCs, either through scripted actions, or in cut-scene anecdotes of their past. MMORPGs don't have any facility for low-bandwidth cheaply-produced cut-scenes.

 

 


 

My current grand-unified theory of multiplayer avatar games

(Back to TOC)

6 September 2006

by Mike Rozak

Discuss on www.mXac.net/forums

Awhile back, I wrote "My current grand unified theory of avatar games" and "Virtual world equation". It is time to update and combine them. (Note: I've presented most of the ideas here before, but this document cleans them up a bit.)

So, without further ado, here is my current theory of life, the universe, and everything... with respect to virtual worlds.

Single-player avatar games (aka: CRPGs, FPSs, adventure games)

A single-player avatar game needs to accomplish the following:

1.         Players enter the game with one or more "goals", many subconscious. These goals might include obvious items, such as: to waste time, to be entertained, or to provide a challenge. Or, they might be more intricately tied to the real world, such as goals to: own a house, make friends, be famous, explore new planets, save the world, etc.

The "deeper" and often subconscious goals, such as the desire to be important to society or to understand oneself, vaguely segue into Richard Bartle's "hero's journey" theory, as described in Designing Virtual Worlds.

2.         One of the game's tasks is to figure out what the player's goals are so that the game can fulfil the goals. CRPGs accomplish this, in part, by letting players choose their race, class, and how they wish to experience the world (such as Oblivion's choice of fighting vs. thieving).

Corollary: The game's genre, advertising, and early user experience should indicate to the player what types of goals the game is capable of fulfilling. A player who cannot properly fulfil his goals in a game will either (a) stop playing and give a negative review to his friends, or in the case of a multiplayer game, (b) try to achieve the goal despite the game, leading to whingeing on the game BBS, or in-game griefing.

3.         The game may provide players with game-specific goals, such as rescuing the princess. Such goals should (ideally) align with the player's goals. To get players to take up game-given goals and internalise them, the game may use "story" elements to add an emotional context to the goal. (Occasionally, the story's resolution may be a goal in itself.)

4.         At any given time, the game should provide players with a menu/choice of goals they work towards. As with a GUI menu, the (figurative) game-menu should be around five choices.

5.         Players should be given in-game abilities that allow them to achieve their goal in a manner that the player expects. This may require the author to allow for several different solutions to the goal.

6.         The time and effort required to accomplish the goal should be roughly what the player expects. Players expect that walking to the grocery store will be easy, but rescuing the princess will be difficult. If a goal is easier to achieve than players expect, they'll complain that game is too easy and/or too short. If the goal is too difficult to achieve, players will give up and complain to their friends that the game was too difficult or boring.

7.         The time and effort to accomplish a goal should be enough to produce a desired effect. For example: If the game is about being an explorer like Magellan or Columbus, then travelling between places might take a relatively long (and boring) time so that players feel like they have travelled a large distance. Another example: Defeating the evil overlord is made pointless if (a) the evil overlord never posed a real threat to the player or (b) the evil overlord never did anything evil to the player.

8.         Don't give players exactly what they expect, either in the process of attaining the goal, or the enjoyment of the goal. If a game does provide players exactly what they expect, they will find the game boring. Instead, give players something better than they expected. (Easier said than done.)

9.         Fun comes into play here; Goals should be "fun" to achieve, and to "fun" to enjoy once achieved. Go talk to Raph Koster about this, or read his book, A Theory of Fun.

10.      By the time a player has completed and enjoyed his goal, a new goal should be found to fill its place. A goal-less player will leave the game.

11.      No matter what, all players will eventually decide that it's time to leave the game... (Except Peter Pan, who never grows up.) Even if the game always has another goal waiting to be achieved, the player will either achieve his personal goal(s), or (subconsciously) decide that the game either won't fulfil/satiate his personal goals any more, or is making the goals too difficult to achieve. (Again, this hints at Richard Bartle's "hero's journey"). In an ideal world, players are able to achieve and enjoy their personal goals.

Also in an ideal world, the player's exit should coincide with an end to the content, so that (a) development money isn't wasted on unused content, (b) the player doesn't feel like he is abandoning content that he payed for, and (c) all the content wraps itself up into a memorable ending.

Multi-player avatar games (aka: MMORPGs, MUDs, virtual worlds)

Multiplayer avatar games are more complex since they not only have all the single-player issues (above), but several new challenges:

1.         Every player has their own set of goals that they bring with them, which isn't any different than a single-player game except...

2.         Players tend to bring their friends into the game, or to leave the game when their friends leave. This means that a multiplayer game must support a much larger variety of goals.

3.         Multiplayer games have a high barrier to entry (because of Internet connection costs/problems and the sometimes negative affects of other players). Therefore, players who play multiplayer games usually have strong social reasons why they want to play, otherwise they would take the easier/cheaper route and play single-player games. These reasons/goals could be as simple as wanting to meet other people, or could be more complex, such as: the desire to run a business with real customers, to be a leader, or to dominate other people.

4.         Players' goals often conflict: There can be only one Napoleon. If two or more players want to be Napoleon, then a mechanism must be in place to determine who gets to be Napoleon. Contemporary MMORPGs rely upon "the grind" as a mechanism; he who whacks the most orcs for the longest period of time is crowned emperor. Real money payments, player skill, or a lottery could also be used.

5.         A player's goals often impinge upon other players' enjoyment of the game. If a player wishes to be an innkeeper, then other players must visit his inn, even though they would prefer to whack orcs and avoid inns altogether. Consequently, some mechanism must exist to ensure that a player's goal doesn't harm another player's experience, and/or if a player's goal does inconvenience another player, the other player should be compensated.

One obvious solution is to make the player work to achieve his goal: For a player to achieve their own goal, they must play a part in helping other players achieve their own goals. Ideally, players won't even know (or care) that they're playing a supporting role. One solution is to create an in-game system that connects players together: People that want to meet other people "coincidentally" meet up in the street. Leaders are "coincidentally" connected with followers. Innkeepers with clientele. Griefers with people that like being griefed (but who won't admit it to the griefers). Etc.

In some cases, the "support" is in the form of higher fees, which end up paying the employees, who end up "altruistically" supporting the player by providing them new game mechanics, better eye candy, or game masters whose role is to make the game more fun.

6.         All of these issues combine to create a complex social ecology. The world must attract a certain percentage of players that want to be followers, a few leaders, and only one Napoleon. If everyone wants to be Napoleon, or everyone wants to be a follower, then the social ecology disintegrates and the game dies. Richard Bartle discusses this in his book, Designing Virtual Worlds.

 

 


 

The hero's journey... kind of

(Back to TOC)

20 September 2006

by Mike Rozak

Discuss on www.mXac.net/forums

Once in awhile, I sit down and write down all the reasons why I think people play (multiplayer) games. I purposely start with a blank page to maximise my creativity. This time around, I scribbled down the following items:

While I wrote my list, several other issues floated through my mind:

"I think therefore I am".... not quite...

All these ideas were intermingling in my brain when a subtle (or illusory?) pattern suddenly emerged...

Several of the reasons why people play games come down to a simple statement, "I exist!" These are:

Descart's one-liner, "I think therefore I am," while profound, isn't appropriate in this case: "My decisions and actions affect the world, therefore I exist (in the world)," seems to be more precise.

I rapidly had another epiphany: "The terrible twos" are all about "I exist!" Two year olds are continually defying parental authority (aka: making decisions), looking for excitement, creating (with crayon scribbles on the walls), and impacting other players (seeking attention by being cute, as well as the less-desirable temper tantrums).

I am a success!

Another collection of reasons for play can be seen as ways that players prove that they're a success:

The pattern begins to emerge...

Consider this:

1.         Children (up to their teens, and sometimes beyond) are often concerned with "proving their existence". They may not be as obnoxious about it as two-year olds, but much of what they do is similar.

2.         Once a child has proven to himself and the world that he exists, sometime before age ten, he notices that everyone else has known this all along, and they all exist too. Furthermore, other people "exist" better than the child; Adults and older children are more skilled at just about everything: being better at basketball, able to ride their bicycle faster, standing several inches taller, etc.

3.         The next logical step is for children to improve how well they exist. Children do this by being competitive... by being a success! (For those of you than don't remember being ten, just about everyone in the world is better at everything you try to do. If this fact weren't obvious enough, school reinforces the impression by continually handing you grades informing you how incompetent you are!)

Children quickly realise that they can't be a success at everything, so they target a few niches and work to be the best; they join the baseball team and are almost immediately better than 90% of their classmates... only because their classmates haven't joined the baseball team. They then work their way up the team, trying to become MVP; if that fails, they try their lot at hockey, volleyball, or swimming. Children eventually find something they can be best at, even if it is something obscure like discus, the chess club, or WoW battlefields.

A second pattern came to my attention: Both "I exist!" and "I am a success!" can be sub-categorised into "introverted" and "extroverted". Making choices, for example, is more introverted because people don't need others to witness the choices for the choices to be relevant. However, a temper tantrum in isolation proves nothing; it must have an audience to be appreciated.

Behold! A matrix:

I exist!

I am a success!

Introverted

  • Make choices
  • Excitement
  • Milestones (levels)
  • Succeeding at goals
  • Small house/business

Extroverted

  • Create/change the world
  • Impact other players (griefing, centre of attention)
  • Fame
  • Level ladders and wealth
  • Large house/business

And now for "the meaning of life"

What happens (in real life) when someone finally is a success (at discus, or whatever)?:

1.         Many people don't ever become a success, but keep striving for their entire lives. If your definition of being success is being the richest person in the world, you are unlikely to succeed.

2.         Many people succeed and immediately choose another goal to succeed at, and then another, and another. How many sports stars become top of their game and then "retire" only to take up another sport or to become a coach?

3.         Most people eventually succeed at something, coming to the following conclusion: I succeeded at being a discus player; I am the best in the world, or at least in my home town. That's pretty good. I could try to succeed at something else, but, in a sense, "I've been there and done that." I know how much work it will take, I know what it will feel like to succeed, and I know I'll have the same "What's next?" dilemma when I'm a success at my new endeavour.

4.         If I am a success and I don't care to repeat the experience, what do I do with my life? Of course, I need to put food on the table and provide shelter, but that still leaves a lot of spare time. What now?

5.         What do famous sports figures do after they retire? Some coach, but not necessarily to prove they're a success. Some start up tennis schools. Some donate their time to charity. Some become politicians. Some just drink themselves to death... Ignoring the alcoholism alternative, the answer is that, "People who have been successful become mentors," or take on similar sorts of altruistic roles.

Introverted people, whom you usually don't hear about, often go back to university, or read every book in the library, or spend all their time watching daytime soaps.

The matrix expands:

I exist!

I am a success!

Meaning of life?

Introverted

  • Make choices
  • Excitement
  • Milestones (levels)
  • Succeeding at goals
  • Small house/business
  • Gain knowledge for its own sake
  • Gain understanding for its own sake

Extroverted

  • Create/change the world
  • Impact other players (griefing, centre of attention)
  • Fame
  • Level ladders and wealth
  • Large house/business
  • Teach
  • Mentor
  • Philanthropy

I have (for the most part) said nothing new

First of all, the transition from "I exist!" to "I am a success!" to "Meaning of life?" sounds suspiciously like Maslow's pyramid of human needs, with "physiological" needs at the base, followed by "security and safety", "love, and feelings of belonging", "competence, prestige, and esteem", "self-fulfilment", and finishing up with "curiosity and the need to understand."

The path also mimic's Joseph Campbell's Hero's Journey to an extent. "I exist!" is the "departure" portion of the journey, "I am a success!" the "initiation", and "Meaning of life?" is the "return".

It likewise vaguely resembles Richard Bartle's "development tracks" from Designing Virtual Worlds, of which there are two common paths:

The new stuff!

I do have something new to say; Let me first discuss a few more observations:

1.         All people exhibit "I exist!", "I am a success!", and "Meaning of life?" behaviours at various times, and often simultaneously. I am painting is very broad strokes.

2.         In real life, children (ages 2 - 16) tend to have "I exist!" behaviours. Adults do too, but the tendency is more prevalent in children.

3.         Teenagers and 20/30-something adults (10 - 40) tend to have "I am a success!" behaviours, beginning with school grades and sports, and finishing with corporate ladder climbing and expensive houses.

4.         On a tangential note: Between ages 20 and 25, most people's brains develop a genuine sense of empathy, which barely exists for children under 15.

To use an everyday example, teenagers (in particular) play incredibly loud music and selfishly impose it on the neighbourhood. Teenagers know that other people don't like them playing loud music, but they don't care. By age 25, loud music playing tends to stop because (IMHO) the culprits can now empathise with their neighbours, who have complaining about the music's volume for years.

The introduction of empathy has an effect on games. As a teenager, I'd happily spend hours slaying orcs in fantasy games. As an adult, I wonder why all orcs are an enemies, and if the virtual orcs have virtual mothers who cry over their childrens' virtual graves. This effect really struck home when I was playing World of Warcraft and a quest requested "10 bear gall-bladders"; in the real world, real bear species are nearly extinct because of the real bear gall-bladder trade! The same goes for shark fin, rhino horns, etc. I found myself empathising with virtual bears.

5.         30-something adults and older (30 +) tend to have "Meaning of life?" behaviours. By the time people hit 40-50, you often hear them asking, "Why do I have such a large house?" and "Why am I working 60 hours a week?"

6.         People tend NOT to change from introvert to extrovert over their lifetime, and vice versa.

What this means:

Loose threads...

I'm not sure I entirely believe these conclusions. They seem a bit too simplistic to be real, and I can readily poke a number of holes in the theory. However, the theory provides some conclusions that need to be explored.

Furthermore, the model doesn't "explain away" all the reasons why people want to play games, such as:

 

 


 

Un-designing Oblivion

(Back to TOC)

30 September 2006

by Mike Rozak

Discuss on www.mXac.net/forums

It's time once again to talk about The Elder Scrolls IV: Oblivion. You might wish to read my previous article about Oblivion here.

This article discusses the "process" used to design Oblivion, or at least the process as near as I can guess. It relies heavily on Oblivion's "Official Game Guide", which I'd strongly recommend to anyone interested in designing avatar games. The book is intended as a walkthrough, but since it describes just about everything in Oblivion, it also doubles as a design document.

Sub-games

Oblivion contains a number of major sub-games:

As well as quite a few minor sub-games:

Oblivion's designers could have used different sub-games, or could have emphasised some sub-games above others. For example, Oblivion does NOT include the following sub-games:

Setting

Oblivion's setting has the following elements:

www.MudConnect.com lists nearly two thousand text MUDs and their web pages. If you browse through them you'll see that almost every MUD has a page devoted to each of the setting items that I listed. However, what most of those MUDs don't do is have the setting items significantly (or creatively) affect gameplay. Being an Elf is no different than being a Dwarf, except for the cliche attribute differences. The few games that eschew Elves, Dwarves, and Orcs still have Elf look-a-likes, and don't seem to realise that an Elf by any other name is still an Elf.

The procedural world

I discussed Oblivion's use of procedural content in my previous article.

To create Oblivion, the designers created the following "procedural" elements:

The reason this part of the world is "procedural" is because:

1.         While a person enters the information by hand, they usually aren't too particular about it. It doesn't really matter if a character is named "Fred" or "George", or where a specific monster is, or if a particular home contains a book of cooking recipes or a book of history.

2.         Once the data has been entered, the author just presses "go" and the world activates. From then on, it requires no intervention from the author or the scripts he writes. (Sounds like a certain religious debate from a few hundred years ago? Does God still play an active role in the world, or did he create it and leave it to its own devices?)

"The procedural world" is a standard component of FPSs, CRPGs, MUDs, and MMORPGs. Most players (and apparently most B-grade game designers) think that all a world needs is procedural content and then it's done. This is far from the case.

Adventure games do NOT have much of a procedural world. In most adventure-game worlds, every detail is carefully placed by the designer. Any details that do not need to be manually placed probably aren't critical to the game and simply aren't added.

"Grind" tasks

One important design element of procedural worlds is the ability to "grind" in them.

Whenever a player is allowed to make choices, they will make mistakes. Some of these mistakes will cause the player to lose/squander resources, like money, equipment, and experience points. Unfortunately, if a player makes too many mistakes, they find themselves without any money, equipment, and/or spare experience points. This lack of resources prevents them from completing the game.

To get around this problem, most worlds have a "backup" so that players can earn back resources that they have squandered. Oblivion, for example, provides an infinite supply of monsters to kill and herbs to collect. However, to ensure that players don't spend all their time playing the "backup" game instead of the real game, the "backup" game is designed so that it doesn't pay (in resources) very well, and is fairly dull. (Plus, there's no reason to make a "backup" fun, or it would be the primary game instead.)

A dull backup game is also known as "the grind".

MMORPGs, for reasons of their own, rely almost exclusively on "the grind". They need the grind because (a) they want to keep players around for as long as possible and can't afford a continual supply of quality (fun) content, and (b) they need a way to "rank" players since many players attracted to MMORPGs like being ranked (above other players, that is). Without liberal amounts of grind, a MMORPG's content would quickly be exhausted by players who routinely spend 20-40 hours per week playing, all the characters would quickly reach the maximum level, and ranking would be impossible.

Adventure games don't include a grind. They avoid the need for a "backup" by making it impossible for players to make decisions that prevent them from advancing in the game. However, adventure games do reduce cost by making many of their puzzles so obtuse that players spend hours wandering around looking for a solution.

Quests

Built on top of the procedural world are hundreds of quests. These are detailed and hand crafted. Each quest has:

Many CRPGs and MMORPGs include quests, although they tend not to be as deep as Oblivion's quests. MMORPGs are especially guilty of having shallow quests, relying on quests that provide a flimsy purpose (aka: FedEx deliveries), no plot, no characterisation, no setting exposure, no cut scenes, no special quest-only regions, no changes to the game world (during or after the quest), no choices in approach, and no choices in outcome.

Conversely, adventure games are almost one hundred percent quests (or technically, quest-arcs). Adventure games don't usually allow for choices in approach or outcome though.

Quest arcs

Oblivion combines a series of quests to create a quest arc. Quest arcs allow the designers to do the following:

Personal NPCs

Oblivion does not have personal NPCs. Adding them would have improved Oblivion. For more information, read this. Basically, you can think of a personal NPC as an extra-long quest-arc that travels with the PC, or at least frequently visits the PC.

Elements of "delight"

A few months back, Raph Koster mentioned adding elements to "delight" players, but not necessarily perform any actual game purpose. (By the way, the same idea was also brought up a few thousand years ago by Aristotle. He called it "spectacle".)

Oblivion contains a few elements of delight:

Interestingly, adventure games tend to emphasise delight more than CRPGs or MMORPGs. The Myst series is a perfect example of this. I'm not sure if it's an accident of history, or if it's because adventure games are targeted at a different player personality, one who appreciates delight more than FPS and CRPG players.

Using the formula to create unique worlds

One major problem that I have with most CRPGs, MUDs, and MMORPGs is that they're basically the same game. They all rely on hit-point based combat. They all have Elves, Dwarves, and Orcs, or races that are just like Elves, Dwarves, and Orcs. They are all based in quasi-medieval worlds with an evil overlord trying to defeat the forces of good. They are all populated by uninteresting quests that involve killing 10 orcs, collecting 10 orc heads, or delivering 10 orc sabres. And the few that have epic quests don't use them properly.

This problem is being further exacerbated by the plethora of MMORPG development toolkits coming onto the market. I'm not opposed to MMORPG (or CRPG) development toolkits; I'm writing my own. I am opposed to toolkits that only allow players to create more of the same. In order for an author to not create more of the same using a toolkit, the toolkit needs to encourage authors to rewrite large segments of the toolkit's code. (See below.) Coding is time consuming and requires skill. Since toolkits proudly boast the ability to create a world in under an hour without any programming skill, they can't require coding, which means they usually don't provide authors the tools and incentives to actually create something unique. What results are thousands of user-created worlds that are essentially all the same.

What is required to create a unique world?

1.         Eye candy is only one of the ways to differentiate a world. Contemporary game developers like emphasising eye candy as a differentiator (to older generations of the genre) because it's a low-risk solution. Throwing out the combat sub-game and replacing it with ballroom dancing is just not done; it is a much riskier proposal that could result in the loss of one's job.

2.         Look at all the bullet points in this article and figure out a unique solution to each bullet item. Not all solutions need to be completely original, but at least a handful of elements should be.

Creating a world full of Elf look-alikes that are just renamed/retextured versions of the classics isn't good enough. Likewise, avoid cliche medieval villages and the same old evil-overlord story.

The lack of creative solutions to sub-games, settings, quests, quest arcs, and personal NPCs is exacerbated by B-grade FPSs, CRPGs, and MMORPGs that fail to include decent quests, quest-arcs, and personal NPCs. (See below.) This only leaves the designers half the amount of "stuff" to play with, and thus, less opportunity for differentiation.

3.         Creating unique sub-games or unique variations on existing sub-games is especially important because the world's setting, quests, quest arcs, and personal NPCs are constrained by the game's sub-games. Unique sub-games require heaps of programming, especially in an eye-candy rich 3D-world.

The least amount of work required is to creatively modify an existing sub-game, such as combat. Making a fighting sub-game that renames "swords" to "katanas" is not going to cut it though. Combat actually needs to be different. How about hit locations instead of hit points? Or intricate strategies like those that are used by real fencers? Or melee combat in zero-G?

A better solution is to demote fighting to a minor sub-game since it's so overused. Instead, place players in a quasi-medieval city (if you like) where the major sub-game is renting apartments to NPCs, or painting NPC portraits... Whatever! Just avoid the cliche of killing hoards of 20 hit-point orcs.

For every major sub-game you add (such as renting apartments), you'll need to either demote or entirely remove another sub-game. Every sub-game you include must be learned by the player. If there are too many sub-games, players will find the learning processes to be too much work, especially casual players who only expect to play the game for ten to twenty hours. Games targeted at hard-core players can include more sub-games. A more casual 15-hour game, such as Beyond Good and Evil, has slightly fewer sub-games than Oblivion, and all of the sub-games are much easier to learn.

4.         You may need new technology. Part of the reason why the same classic sub-games are continually reused is because that's all that currently technology allows. A game controller only has so many buttons and knobs, limiting what kind of inputs are possible. The same problem exists for outputs. The only reason Guitar Hero and Dance! Dance! Revolution have unique sub-games is because they each include new I/O devices shipped in the box!

Text-to-speech and speech recognition are two technologies that enable new sub-games. I discussed these in The NPC-conversation wall.

5.         Properly designed quest, quest arcs, and personal NPCs are required to create unique worlds. Most novels and television shows are set in the real world, but they still manage to differentiate themselves from one another. They do this by focusing on different combinations of plots and characters (aka: personalities); in avatar games, plots and characters are part of the quests and quest arcs, not the procedural world.

 

 


 

Stickybeaking

(Back to TOC)

20 September 2006

by Mike Rozak

Discuss on www.mXac.net/forums

This article contains some thought experiments involving games, puzzles, toys, stories, socialisation, Choose-your-own adventures, and stickybeaking; You never thought they were related, did you?

Games

World of Warcraft and Everquest II are clearly games. In both of them, there exists a landscape populated by monsters, non-player characters, and other players. The core activity for players is the combat sub-game; its needs drive the AI and social interaction designs.

Let me put forth a non-binding definition of a game:

A game includes a reality bound by a set of rules. Players make choices and the rules determine the outcome, ultimately resulting in the player winning or losing. In a game, the degree to how well a player's choices worked result scalar positive or negative feedback. Players use this feedback to improve their understanding of the system as well as hone their abilities (such as manual dexterity). With players' new feedback and skills at hand, players make another choice. Repeat ad infinitum until players understand the rules and get bored. (See Raph Koster's "A Theory of Fun" for more info.)

In WoW and EQII, players try to kill monsters. The game returns positive/negative feedback in the form of hit points and other metrics. Players gradually perfect their combat techniques. To keep the game interesting, variations are added into the game over time, such as new monsters or new PC abilities.

In artificial intelligence terms, the described gameplay sounds an awful lot like neural networks. Neural networks work by making a choice, seeing how right/wrong the choice was, and adjusting their model of reality based upon the amount of positive/negative feedback. Over many thousands of iterations, a neural network learns to accurately predict the input data.

A world without games?

What would happen if gameplay were removed from WoW and EQII? What would players do?

Both WoW and EQII are missing some activities present in other virtual worlds, such as puzzles and toys:

Puzzles

Neither WoW nor EQII have any puzzles to solve.

In many ways, puzzles are the opposite to games. In a puzzle, players make one choice. The feedback is binary, either "You got it wrong, try again!" or "You got it right!". Failure just returns players back to square one, while success sends players on to their next puzzle. The same puzzle is never reused because it's trivial to solve the second time around.

Consequently, the neural-network approach of learning from the positive/negative feedback doesn't work. The binary feedback from puzzles, while accurate, is only TRUE or FALSE, and isn't good enough for such learning. To solve a puzzle, players must understand what the puzzle is about, and then the solution is easy. Neural networks cannot solve puzzles at all well. At the moment, the only way that computers can solve puzzles is to brute-force them and try all possible combinations.

As I've said before, puzzles utilise deductive reasoning. CRPG/MMORPG gameplay is inductive.

Puzzles don't make terribly great social games since any players that know the solution have to bite their tongue while the other players in the group struggle to find the solution. In WoW's game-based combat, killing the same orc a second time isn't much easier than it was to kill it the first time.

Toys (aka: play)

A toy is a reality bound by a set of rules, some of which are fixed, and some of which are liberally added or removed by the player. The player makes decisions and sees what happens, getting vector feedback that isn't just success or failure. Based on the feedback and their own whims, players can make new choices, or they can change the rules of the toy. Repeat ad infinitum until the player runs out of choices and/or new rules to invent and gets bored.

For example: A frisbee is a toy. It is bound by the rules of physics, albeit interesting ones that allow it to fly in ways that it shouldn't. To the laws of physics, players often add their own rules of throwing it up in the air and catching it, or throwing it from person to person, or only being able to catch it in one hand. (Some of these rules temporarily convert the frisbee from a toy to game.) During play, players are continually making choices (throwing the frisbee to different people and in different ways) and changing the rules ("Left handed catches only!").

Toys are about creativity and experimentation. Games are about winning.

A matrix?

I feel a three-dimensional matrix coming on:

Goal oriented

Inductive / intuitive

Deductive / logical

Learning (input)

Game (Tennis, CRPG)

Puzzle game (Adventure games, Chess?)

Creation (output)

Work (Artist)

Work (Engineer)

Experimental

Inductive / intuitive

Deductive / logical

Learning (input)

Toy (Frisbee)

Toy (Puzzle)

Creation (output)

Toy (Musical instrument)

Toy (Legos)

I'm not entirely happy with the matrix, particularly the part about goal-oriented creation always being "work". Also, the matrix implies a hard separation between inductive and deductive reasoning, learning and creation, and goal oriented vs. experimental; Many activities cross over. Chess, for example, is a combination of looking at the board and instantly understanding what's going on (inductive), as well as trying to understand your opponent's strategy and predict their future moves (deductive).

I can even go a step further with the matrix: "Stickybeaking" is about learning fun/interesting information either by seeing/experiencing something directly (such as the neighbour's new car) or being told about it (a salacious rumour). Stickybeaking is neither inductive nor deductive since it's about acquiring knowledge rather than understanding it.

As a side note: The urge to stickybeak is enhanced by in-game "stories." The whole point of a story is to make a narrative as fun and interesting as possible in order to keep readers/viewers interested in the activities of fictional characters. Stories accomplish this by using several techniques, such as suspense, likeable characters, and conflict. The same techniques can encourage stickybeaking.

If you really squint, "socialisation" is, in small part, the inverse of stickybeaking. It includes the creation of rumors (for stickybeaks) as well as many of the elements (likeable people and conflict) that stories try to mimic... but this is a stretch.

Goal oriented

Understanding

(Inductive / intuitive)

Knowledge

Understanding

(Deductive / logical)

Learning (input)

Game (Tennis, CRPG)

Research

Puzzle game (Adventure games, Chess?)

Creation (output)

Work (Artist)

Work (Reporter)

Work (Engineer)

Experimental

Understanding

(Inductive / intuitive)

Knowledge

Understanding

(Deductive / logical)

Learning (input)

Toy (Frisbee)

Stickybeaking

Toy (Puzzle)

Creation (output)

Toy (Musical instrument)

Socialisation, rumors

Toy (Legos)

Since I'm not entirely happy with the matrix, I don't use the matrix in the rest of this document. I decided to bring it up, though, because it's an interesting concept to consider.

Beyond DikuMUDs (WoW and EQII)...

As I stated above, WoW and EQII are about:

At the moment, 15+ million people play game-like MMORPGs.

What other types of worlds are possible?

Second Life

Second Life is:

Second Life is the dominant social/creation MMORPG. While nearly a million people have tried Second Life, only around 100K are active players.

Uru Live

Uru Live is:

Of course, Uru Live was cancelled before it was released. It's currently being revived again, but my suspicion is that it'll have fewer players than Second Life, perhaps 10K players. The reason is that puzzles don't encourage socialisation as much as games or toys.

Hypothesis: Games don't mix well with puzzles because games attract "achievers", players that like to "win" and be "ranked" above other players. Puzzles can be easily "solved" by downloading a game walkthrough over the Internet. Therefore, puzzles are wasted effort in the eyes of achievers, the primary audience for multiplayer games... This doesn't mean that puzzles and games can't be combined, just that they can't be combined with achievers.

World-like MMORPGs

World-like MMORPGs (Ultima Online, the old Star Wars Galaxies, and Runescape(?) ) are:

If Runescape is included as a world-like world, then there are around one million world-like players. Without Runescape, the number is closer to 400K.

Hypothesis: While world-like worlds are more popular than Second Life, they are less popular than game-like worlds. Could this be because (a) games are more popular than toys, and/or (b) trying to fill a world with too many different types of activities results in lower quality activities due to design conflicts? For example: Ultima Online cannot allow players as much building freedom as Second Life because that much freedom would break Ultima Online's gameplay.

Hypothesis: World-like worlds traditionally simulate a world and encourage players to "live" there, spending 20+ hours a week in the game. Could world-like worlds actually just be a sub-set of "toy + game" based worlds, some of which don't require such a large time commitment?

Chat room

A chat room is a world that handles only:

While chat rooms are popular (way more than 15 million users), they're "a dime a dozen".

There's no particular reason for a player to use one chat room above another, other than the other people in the chat room. Consequently, some chat rooms include built-in games, toys, puzzles, and stickybeaking to attract users... which turns them back to a DikuMUD, Second Life, or Uru Live.

Stickybeak world

Imagine a world where all players do is:

Notice that there aren't any games, puzzles, or toys.

Would this work? I'm not sure; there aren't any worlds that follow this model. Stickybeaking synergizes with socialisation, but not as well as games or toys. Furthermore, producing content for stickybeaking is fairly expensive since it is quickly consumed; that's why Second Life has players build their own stickybeak content as part of the building "toy".

Choose your own adventure... A stickybeak world?

When I was a teenager playing the Tunnels & Trolls solitaire adventure, City of Terrors, I thought about turning it into a computer game. City of Terrors was basically a Fighting Fantasy book, or Choose-your-own-adventure book with combat thrown in.

From time to time, I still consider a computerised CYOA game, but I don't think it'll work. There are a few reasons:

1.         A CYOA "page" is a cut-scene followed by a few choices. Each choice leads to another page. In a T&T solitaire adventure, Fighting Fantasy book, or CYOA book, about a third of the pages are about movement. Movement is better handled by modern game movement, either room-to-room or WASD free-form movement.

2.         About a third of the pages involve combat or traps. These are games and puzzles (as per my definition above). However, combat and traps are best handled by placing them in rooms/spaces that player characters move in, not in CYOA pages.

3.         Items (1) and (2) create an experience that's similar to a CRPG, MMORPG, or adventure game, except that one-third of gameplay is spent in CYOA "pages", which amounts to cut scenes followed by choices. The cut-scenes are an incredibly useful design tool because they enable more interesting NPCs (story) as well as describing events that the world's physics and animations engine can't simulate... "Mary sits down daintily and carefully sips at her tea, saucer held gently in her left hand."

4.         However, most players hate cut scenes because cut scenes temporarily take the control away from the players. Technically, games take control away from players all of the time, but only for a fraction of a second, a few seconds at worst. Players don't mind that, which (I suspect) means that short cut scenes are acceptable. However, a CYOA with short cut scenes implies more decisions.

As devil's advocate, most game players don't care about the "story" that cut scenes can create, so they're biased. Many people are enthusiastic about stories... but those people are almost all watching TV or reading books, not playing games.

5.         More decisions result in more branching, which costs exponentially more money to develop. The only saving grace is that each individual CYOA (text) cut scene and decision list is cheap to develop. An animated CYOA "book" with frequent branches would be incredibly expensive.

6.         Having so many choices becomes problematical because there are important design rules to follow, as I list in my choices writeup.

For example: Many of the choices in CYOA books lead to dead ends... literally. In a CYOA book, if players select a dead-end choice, they just flip back to their previous page and choose something else. In a computer game, players can't flip back; that would be cheating and isn't allowed by the game's UI. If dead ends are common, players will just download the walkthrough in order to avoid choosing a dead end. Once they have the walkthrough in hand, they'll forgo choosing altogether and just follow its instructions.

7.         Consequently, (almost) all choices must lead to equally interesting/positive consequences. This results in a lot of branches that never reconnect with the main branch, which is even more development work.

8.         With so many branches, players will want to replay each scenario several different times to see what happens in each. What happens if I decide to go on a date with Marry instead of Jane? This turns the experience into a stickybeak game where players explore possible futures instead of exploring three-dimensional space.

However, if players do replay scenarios then they're at least re-using content, reducing per-hour play costs.

9.         Exploring alternate realities is a strange and obtuse concept for most people. After all, the last time a player tried the scenario, Mary tragically died in a car accident after the prom, but she's alive an kicking once again in this new reality. I suspect that most players will be turned off by the oddity.

An easier-to-stomach design is to create template scenarios with replaceable names, so that Mary is renamed to Alice, and Jane to Betty. However, because all the other text/animation is the same, players will quickly realise that the same scenario is being rehashed.

Having said all that, CYOA pages still have merit. They are, for example, the standard mechanism for NPC conversations. They may not be enough to base a stickybeak game off of though.

A parting thought...

This has almost nothing to do with puzzles, games, toys, socialisation, and stickybeaking, but I thought I'd bring it up anyway.

1.         As stated above, CRPG and MMORPG players like inductive games. Adventure-game players like deductive games.

2.         The preference is probably a result of the way that players (like to) think. Thus, CRPG/MMORPG players prefer to use inductive reasoning, while adventure-game players prefer deductive reasoning.

3.         Game developers/designers that like to play CRPGs/MMORPGs will try to find jobs in CRPG/MMORPG game companies. Likewise, adventure-game enthusiasts are more likely to work for adventure-game companies.

4.         When it comes time to designing a new CRPG/MMORPG, do CRPG/MMORPG designers prefer an inductive design? (Model the game after a previously successful game, but with a few changes.) Do adventure-game designers use deductive design? (Try to understand, at a fundamental level, what the player expects and what the technology can deliver, and design from that.)

While I've seen plenty of evidence of inductive (evolutionary) designs for CRPGs/MMORPGs. I don't see an overwhelming amount of deductive design from adventure games; they seem to be as evolutionary (not revolutionary) as CRPGs/MMORPGs.

 

 


 

Neverwinter Nights 2 Analysis

(Back to TOC)

8 December 2006

by Mike Rozak

Discuss on www.mXac.net/forums

This is not a review of Neverwinter Nights 2, and isn't intended for people deciding whether or not they wish to purchase the game. Instead, it is an analysis of some of NWN2's design. For my other analysis, see:

Warning: There are some game spoilers below. Don't read any further if you haven't already player NWN2 and still plan to play it.

Important design elements

NWN2 has two design elements that really stand out:

1.         Cheesy cut-scenes - While cut scenes are standard for all games, NWN2 uses a low-cost cut-scene engine that produces cheesy-looking cut-scenes. However, the low cost-of-production enabled the content designers to include a large number of cut scenes and associated branching narratives (aka: Choose Your Own Adventure books).

2.         Personal NPCs - NWN2 does an excellent job implementing personal NPCs.

Cheesy cut-scenes

Cut scenes are standard fare in all games except Tetris. They are incredibly useful for fleshing out characters and producing sympathetic goals in players.

Most games have a dedicated team that produces high-quality eye-candy-laden cut scenes that cost an awful lot of money. Consequently, they don't include many cut scenes, and they certainly don't include the cut scenes in branching narratives because half of the (very expensive) cut scenes would never be seen by players depending upon which branch they choose.

Neverwinter Nights 2 took a different approach: They include a toolkit for cut scenes as part of their engine. This toolkit isn't very powerful, and it doesn't appear that there was a specialised NWN2 cut-scene team, so the cut scenes are very cheesy looking. However, what it lost in eye candy is gained in power.

Because NWN2 cut scenes are so cheap and easy to produce, NWN2 includes heaps of cut scenes, using them to not only create sympathetic goals, like all cut scenes do, but to create new gameplay.

The new gameplay is possible because the cut scenes are so cheap they're disposable, and ultimately mixed in with branching narratives (in the form of dialogue trees). Traditional (expensive) cut scenes aren't used with branching narratives because that would mean that players wouldn't see half of the expensively-produced animations. (I'll provide some examples of NWN2's cut scenes later.)

To top it off, NWN2's cheesy cut-scenes are dynamic! That means they're customised depending upon which party members (personal NPCs) are tagging along with the player. A traditional cut scene can't compete against that.

Personal NPCs

Awhile back, I wrote up an article about personal NPCs. One of my inspirations was Baldur's Gate, a direct ancestor of Neverwinter Nights 2. Baldur's Gate used personal NPCs as party members.

NWN2 takes the idea of personal NPCs a few steps further than Baldur's Gate:

Unfortunately, NWN2 party members:

Both of these problems emphasise that there are two halves to NWN2, the cut scenes and their branching narrative, and the "kill lots of monsters" game.

The tightrope game

Whenever the player gets into a conversation with a NPC, players are provided a handful of responses, many of which are different ways of saying the same thing. One response might be polite, another bold, and a third intimidating.

Many games provide a variety of choices so that players think they have a choice, but in reality, all the choices end up in exactly the same place with exactly the same consequences. This is a no-no, as I pointed out in Choices 3. It's equivalent to asking a player to chose between two doors, each of which leads to the same room.

NWN2's conversation-tree choices actually have different effects:

When combined together, all the conversation-tree dialogue choices create a "tightrope game". Every time a player is asked to choose, they must weigh up all the outcomes of the choice.

The trial

Half way through the game, the player is put on trial using a lengthy series of cutscenes. The trial is a nice invention because:

Unfortunately, NWN2 designers messed up the fundamental design of the trial in two important ways:

1.         The player doesn't know that the trial is going to happen and that his actions are going to be judged. Without such knowledge, the designers are effectively asking the player to chose between two identical doors, which as I wrote in Choices 3, is bad design. This problem is somewhat mitigated by that fact that dialogue choices also have more immediate effects on NPCs and personal NPCs.

2.         If the player wins the trial, the enemy lawyer asks for trial by combat. And although I haven't verified this, if the player loses the trial, the player's lawyer asks for trial by combat... Which means that the trial doesn't affect the outcome of the game and is equivalent to two doors leading to the same room, another design flaw.

The castle

Later in the game, the player is given control over an old castle, another refreshingly-different sub-game. It is the player's responsibility to:

I haven't played the castle sub-game to completion, but I can already see a flaw:

The importance of cheesy cut-scenes and personal NPCs

In my opinion, if NWN2 didn't have the cheesy cut-scenes and personal NPCs, it would be a much weaker game, so weak that I would have stopped playing after only a few hours. Oddly enough, many reviewers didn't seem to notice either of these design decisions; perhaps they didn't consciously recognize them, or perhaps they found the traditional "kill lots of monsters" game to be adequate.

Also in my opinion, if NWN2 had cut out 75% of the "kill lots of monsters" content, it would have been a better game. Not only would the length have been shorter (NWN2 is too long), but I've killed way too many orcs in previous games for it to be fun any more, including Neverwinter Nights 1, and Baldur's Gate, and World of Warcraft, and Everquest II, and Oblivion, and Fable, and Might and Magic, and Dungeon Siege, and.... and going back 25 years to Wizardry I and Ultima I.

 

 


 

IF Title Design 101

(Back to TOC)

13 December 2006

by Mike Rozak

Discuss on www.mXac.net/forums

This article is a follow-up to my Quest design 101 writeup, and attempts to describe how to design an interactive-fiction title. The same guidelines apply to CRPGs, MMORPGs, FPSs, and other avatar games.

The player can change the world in meaningful ways...

By the time the player "completes" the IF title, they should have changed the world in a "meaningful" way:

The world changes the player in meaningful ways...

The world should change the player, not just the player's character:

Players can change and be changed by other players...

If the interactive fiction title is multiplayer, then:

Change, in general

Not all players are interested in the same amount and type of change. Using a distortion of Richard Bartle's player models:

Immersion

The world should be immersive:

 

 


 

Quests, stories, and spaghetti

(Back to TOC)

2 January 2007

by Mike Rozak

Discuss on www.mXac.net/forums

Recently, I've been pondering methodologies used to design virtual worlds (and avatar games in general). I've seen the following methodologies used in games:

I happen to like stories, but I don't like the way they're handled by any of the existing methodologies. Myst-like and linear-story games are very limiting as far as choice. The spaghetti (Oblivion) methodology creates a lot of freedom, but ends up being a homogonous entanglement of quests. Diku-MMORPGs only have token stories. Pre-Diku text MUDs and world-like worlds don't need any story whatsoever.

Here's an alternative that I'm currently thinking about:

Stories/quests with meaningful and reconnecting choices

For the moment, I'll just discuss quests (sub-stories), but they'll tie into the quest-arc(s) that control(s) the game's overall story.

To produce a quest:

1.         The author writes a story that the player will participate in, much like a game based on a linear story.

2.         Unlike a linear game, each quest includes meaningful choices that allow for branches within the story that ultimately change the story's outcome, just like Choose Your Own Adventure books. Unfortunately, meaningful choices are a lot of work, particularly if players are offered several meaningful choices in a row. If each meaningful choice has two branches, and there are three meaningful choices throughout the quest/story, then there are 2 ^ 3 = 8 outcomes. With four choices, there are 2 ^ 4 = 16 outcomes! Thus, don't expect stories/quests to have any more than two or three meaningful choices.

3.         Choices that reconnect don't produce as many branches, such as the choice of travelling to Inviroth by land or by sea. Each choice produces a different experience, but ends up back at the same node. Consequently, quests/stories may include several reconnecting choices early on, before there are too many branches (a consequence of meaningful choices).

If a quest/story only includes two meaningful choices, then each quest would have a beginning, middle, and end. The beginning only has one alternative (or branch). A choice is made at the transition between the beginning and middle. There are two alternatives/branches for the middle part of the quest. Another choice is made at the transition between the middle and end, creating four alternatives/branches for the end-quest.

Add in a few reconnecting choices at the beginning and middle of the quest/story, but not the end, and the author then has to write two beginning branches, four middle branches, and four end branches (with no reconnecting choices). That's a total of ten branches.

A quest with no middle would only have four branches, two reconnecting choices at the start, and two meaningful choices at the end.

Procedural choices

For some reason, Choose Your Own Adventure books aren't very satisfying (to me). I've spent a long time trying to figure out why, and (I think) I finally have the answer:

Players like to make frequent choices, as often as one choice every few seconds. It's part of what makes the world immersive.

CYOA books "fail" because players only make infrequent choices; they spend a minute reading a page, make a choice, spend another minute reading, make a second choice, etc.

Theoretically, if a CYOA book could offer me a choice every sentence or two (minus the hassle of reading the choices and flipping to the next page), the experience would be quite compelling. It would also result in several quintillion branches for the author to write!

Procedural choices (managed by software algorithms) can easily offer players frequent choices. Unfortunately, procedural choices tend to be shallow and less interesting than the hand-created choices in CYOA books.

The solution (or rather, a solution) is to fill in the time between meaningful choices (CYOA-book choices) with procedural choices. Procedural choices might take the following forms:

Some elegant uses for procedural choices are:

The three-act game

The three-act story is well known. There's a beginning, middle, and end, each with distinct boundaries and each act having a different theme. In the Hero's Journey, for example, the first act is about the character's "call to duty", followed by trials in a distant land, and completed by the journey home with the boon.

Most games with linear stories have three acts. Even some MMORPGs have three acts; for example: World of Warcraft's first act is about the player doing quests for their village and race/clan (at the request of NPCs), the second act is hoarde vs. alliance (often at the request of NPCs), and the third is guild vs. guild.

Basically:

Not coincidentally, quests (as I described them) have three acts, with a meaningful choice between each act. Thus, the entire game can be seen as one large three-act quest. Or, it can be seen as a fractal-like construct with three-act quests within three-act quests.

Tailoring quests to each act's theme

As I've written before, players should have (as much as possible) a choice about which quests to undertake, and a choice about what order to undertake them.

Oblivion does an excellent job providing such choices, but at a cost. While players can undertake (almost) any Oblivion quest at any time, Oblivion only has one act. It can't have more than one act because it doesn't place limits on what order players complete the quests.

At the other extreme, completely linear games like Syberia have three (or more) acts, but no choice about which quest to undertake.

In the middle is World of Warcraft. Quests are level-limited, so a player character's level (aka: progress through Act I, II, and III) affects what quests can be taken. To get to the next level, a player must complete (to pick a random number) 20 quests. Since WoW provides 40 quests per level, players can choose which quests they wish to complete, and in which order (to an extent).

Personally, I'm interested in producing a game without levels. It will have skills, but the difference in power between a low-skilled and high-skilled character is minimal. (Skills are actually used as a time-based resource, but that's for another article.) Thus, I can't use levels to limit access to quests, much the same limitation as Oblivion, which automatically adjusts all quests to suit the PC's level.

However, I don't want Oblivion's homogonous mass of intertwined quests (aka: spaghetti). I actually want to have acts.

My current solution is:

1.         Some quests will be act-specific, and won't be given to players unless they're in the proper act. (Again, similar to WoW.)

2.         Some quests will change depending upon the player's act number. Ultimately, this means that some quests will require three thematically different versions, one for each act. The version handed out depends upon the what act the player is in when they accept the quest.

3.         Players won't have to complete all of the available quests for an act. As in WoW, they'll have a choice of quests.

4.         The game's story allows a transition from Act N to Act N+1 only when enough quests from Act N have been completed (much like WoW). In order to complete the transition, the player will need to finish a transition-specific quest, such as a travelling quest, from the hub-town for Act I to the hub-town for Act II.

5.         Taking a page from Oblivion, there will be one main quest-arc that progresses through all of the acts, and it (potentially) includes sub-quests that cause the transition between acts. These sub-quests won't be available unless enough act-specific quests are completed (as above.)

6.         There may be a few secondary quest-arcs, also like Oblivion.

7.         There may be many one-off quests, like Oblivion.

Storylines (large games)

Although I don't expect to have the resources for this, a large game might include several different storylines. Each storyline is the equivalent of a three act game, but they share the same world. For example: One storyline could be "default the evil overlord" while another might be "become the mayor of the city".

Filling the world with multiple storylines has the advantage that players are never entirely sure what the motivations of other players are because they might be on a different storyline.

To reduce development expenses, most quest-arcs and quests would be shared amongst all storylines.

 

 


 

Programmed intuition

(Back to TOC)

10 January 2007

by Mike Rozak

Discuss on www.mXac.net/forums

In this article I will revisit a way to visualise choice and use the discussion to show how "intuition" can be a useful device.

Choice space, part 1

Imagine, if you will, a multidimensional volume of "choice space" that covers all the possible choices that players can make. Since visualising a highly-dimensional volume is too complex, try to imagine a piece of paper. X (left and right) represents choices, and Y (up and down) represents time.

1.         A story is a line, since players have no choices. It starts at the bottom of the page (the past) and works its way up. It may make jogs to the left or right, but it's still a line.

2.         A "Choose Your Own Adventure" book, with a set of choices, is a tree. It starts at the bottom of the page with the first page of the CYOA book. At each choice is a node with branches to the left or right. Technically, CYOA books aren't trees because branches sometimes recombine, but a tree is an easier concept to envision.

3.         If there are sub-games between the choices (aka: procedural choices), then the line segment between each choice becomes fuzzy, representing all the possible ways that individual players could complete each sub-game. If players always start and finish at a well-defined CYOA-choice then the fuzzy lines refocus into a point at the choices.

4.         If the procedural-choices are used to determine the CYOA choices, then the fuzzy lines DON'T refocus to a choice-dot, and remain fuzzy even at branches. For example: If the player's choice of whether to be good or evil is actually determined by their procedural choices, then the choice node isn't a dot, but a fuzzy ball.

Choice space, part 2

Instead of viewing choices as a series of fuzzy lines, imagine that the two-dimensional sheet has a third dimension of topography added. Lines turn into steep gorges, while fuzzy areas are valleys.

However, in my current description, even the valleys are walled by steep cliffs, preventing players from climbing out.

What I describe is the typical configuration for contemporary adventure games and (most) CRPGs. Basically, players can wander around in the valleys, completing quests, but are prevented from ever climbing out of the valleys. For example: Players cannot decide to join the evil overlord, much less become his hairdresser.

Some choices are just not allowed:

1.         Some choices aren't possible due to limitations in the programming of the world's physics, such as becoming the evil overlord's hairdresser. There is no hairdressing skill, and only a few unstylish 3D hairstyles like Mohawks and mullets.

Adventure games, which tend to have exceptions-based physics, commonly prevent any activities except those that advance the plot because such activities haven't been programmed in.

2.         Some choices will be be prevented by other design assumptions; even if the world allowed hair styling, the author may have pre-programmed the evil overlord to always be antagonistic towards the player character... meaning that he'd never show up for his hair appointment.

3.         Some choices haven't been developed because the author doesn't think players would be interested in them. The evil overlord might show up for his hair styling, sit patiently in the chair, and even pay the character, but nothing more would come of it; he'd be just another customer.

4.         Some choices are developed, but not as well as they could be. The overlord may become a regular customer, but the overall hair-stylist quest/plot is fairly boring. You can think of these as short valleys. The overlord might get a few haircuts throughout the game, but his hair wouldn't undergo a character arc or anything story-like.

5.         Some choices are disallowed because they would ruin the game's story and/or some pre-programmed quests. See below...

Problems with total freedom

Some choices are disallowed because they would ruin the game's story and/or some pre-programmed quests. For example, the right haircut might make the evil overlord so happy that he stops his evil ways. Or, to get away from the hairdressing motif, what would happen if a player in a Lord of the Rings CRPG is given the one-ring for safe keeping but decides to flush it down the toilet? Game over.

In other words, if a game lets a player climb out of the valley then the player might not be allowed back in. Once the one ring is out of the player's hands, there's nothing the player can do except slay orcs. All the work that the authors spent digging out the valley to Mordor is lost on the player.

Given a choice, players will always (accidentally) climb out of the valley. Don't forget, they're wandering around in a thick fog and can't see where they're going. Even if they don't want to break the story by leaving the valley, they will do so.

Adventure games and CRPGs solve this problem in the following ways:

Intuition

There is another possibility, but it breaks the fourth wall!

If a player does something that would ruin the plot (or even a minor quest), warn them. Display a message, "Flushing the one ring down the toilet is a bad idea. Do you still want to pull the lever?"

Providing players with "intuition" warning has the following benefits:

1.         Players can do whatever they want (so long as the world's physics allow for it). This means that they aren't restricted (barring limits to the world's physics). In other words, they can climb out of the valley.

2.         A player that is told, "You can't flush the one-ring down the toilet," gets frustrated. If they get an intuition warning instead, they may be annoyed for having the fourth wall being violated, but they now know: "I can't flush the ring down the toilet for my own good."

3.         Despite the warning, players can still take quest-damaging actions. Oddly enough, this provides them with another choice; they can choose to rebel.

"Intuition" is commonly used in MMORPGs, but in a different form. Monster names are coloured based on their difficulty, essentially telling the player, "You have a bad feeling about attacking that monster."

The plot / valley thickens

The topology analogy has some ramifications for a game's design:

 

 


 

Interactive fiction equation

(Back to TOC)

10 January 2007

by Mike Rozak

Discuss on www.mXac.net/forums

For awhile now, I've been trying to identify a fundamental "equation" that can represent many (or most?) of the design problems found when creating a virtual world. This article is my latest attempt. It focuses on the tradeoff between story and choice.

The prescient-author thought-experiment

Like Maxwell's demon, I'll begin my discussion with an incredibly improbable / impossible thought-experiment.

Imagine that an author is creating an interactive fiction title. The author can see the future, happens to know exactly what choices a specific player will make long before the IF title has even been started. This prescience saves the author a lot of time and work because the author only has to write a piece of linear fiction.

Because the author has (potentially) years to design and implement the game (which ends up being just a story), the game can even include movie-quality eye candy.

Once the title is completed, the chosen player is placed in front of the computer, given a mouse and keyboard, and chooses away. However, all the player's choices are effectively ignored during "gameplay" because the author knew exactly what they would be before hand, and programmed them in. Regardless, the player walks away from the "game" vowing that it's the best one that he's ever played (assuming that the author is any good).

Let me start "the equation" here.

E(c) = f(author's skill, eye candy)

In English, the player's enjoyment of the game's choice, E(c), resulting from a given choice, c, (and its subsequent story/experience) is a function of:

The "game-master behind the curtain" thought-experiment

Prescient authors are hard to come by. However, face-to-face RPG game masters (or "dungeon masters") are more common.

Instead of a prescient author creating a movie ahead of time, imagine a game master sitting at the other end of a network and acting as the IF title's brains. This game master is, of course, incredibly skilled, and can parse the player's inputs so blindingly fast that the player thinks they're interacting directly with a computer. Furthermore, the game master can create new content in the blink of an eye.

Even if the game-master's authoring skill and eye-candy ability are the same as the prescient author's, the player's experience won't be as good:

Thus, E(c) is added to:

E(c) = f(author's skill, eye candy, knowing what the player will do ahead of time, enjoyment synergy, real-time restraints, published tie-ins, other players)

Outsourcing the game-master with AI

Game masters are expensive, being paid at least minimum wage. Therefore, the bean counters will get rid of them, replacing game masters with immigrant AI labour. AI gets paid very little and doesn't require benefits or holidays.

Unfortunately, contemporary AI has plenty of flaws compared to a game-master:

E(c) is expanded once again:

E(c) = f(author's skill, eye candy, ..., a compelling story, etcetera)

E(c) rules of thumb

To look at E(c) another way, the following factors affect E(c):

You can't do that!

Unfortunately, not every action that a player chooses to undertake can actually be acted upon. Character limitations aside, players encounter the following problems:

This is an important part of "the equation":

P(y|d) = f(no content, world's physics, guess the verb, adversely affect other players, break major or minor quests, not really a choice)

In English, the probability of encountering a "You can't do that!" (or similar) situation, y, given a decision, d, is a function of how much content there is, how robust the world's physics are, etc.

Just to clarify, players will also be told, "You can't do that!" when they try have their human character (or pig) fly, try to walk through walls, or leap tall buildings in a single bound. This sort of failure isn't an issue for P(y|d) as long as players accept it as part of the reality of the world.

The annoyance factor

When players choose an action that isn't allowed, they get frustrated and annoyed, and they don't enjoy the game as much.

How annoyed a player gets depends on a few factors:

The annoyance factor produces another term:

A(c') = f(expect game to handle choice, why the choice didn't work)

In English, the annoyance factor, A(), given a failed choice, c', is a function of the player's expectation that the game should handle the choice, etc.

P(y|d)A(c') rules of thumb

To look at P(Y|d)A(c') another way, the following factors affect P(Y|d)A(c'):

First pass at the equation

My first pass at the "interactive fiction equation" is:

E = Sd (E(c) - P(y|d) A(c')) I(d) P(d)

In English, the player's overall enjoyment of the interactive-fiction title is the sum, over all decisions, d, of the enjoyment from the individual choice that's finally accepted, E(c), minus the probability of the player's choice being invalid times the annoyance that results.

Both the choice-specific enjoyment and annoyance are scaled by I(d), which is a function that indicates how important the player views a decision. (See the "leaf picking" example above.) The are also scaled by P(d), which is the probability that the player will encounter the decision. Thus, the more branches in the game, the lower the probability that the player will encounter the specific decision.

The equation is busted

From the start, let me emphasise that the equation is busted. It's only an approximation. Here are some of the more obvious flaws:

The less-busted equation is:

E = Sd ( E(c) - P(y|d) A(c') - A(c, t) + ki ) I(d) P(d)

Interpreting the equation

The obvious use for the equation is to maximise E for all (paying) players, limited by the funds and technology that the interactive fiction title has available. This means:

Some specific conclusions about E(c), how enjoyable the consequences of a choice are:

About P(y|d), players getting "You can't do that!" messages:

About A(c'), players being annoyed by "You can't do that!" messages:

About A(c, t), players being annoyed by long cut scenes:

About ki, immersion from choice:

About I(d), decisions that the player perceives as important:

About P(d), common decisions:

 

 


 

Interactive fiction vs. games (and world-like worlds)

(Back to TOC)

12 January 2007

by Mike Rozak

Discuss on www.mXac.net/forums

I thought I'd spend some time discussing how I think interactive fiction, games, world-like worlds (like Ultima Online), creation-worlds (like Second Life), and role-playing intensive worlds (RPI) relate to one another.

Games as a base, adding interactive fiction

Imagine a game, like a fighting game where players stay in one room and kill hoards of aliens that mindlessly attack. This game is the basis for CRPGs and MMORPGs. Similarly, puzzles are the basis for adventure games.

However, to make the game more fun and meaningful, authors try to create sympathetic goals, which explain to the player why they're killing all those monsters or solving all those puzzles. These goals inevitably take the form of a short story explaining that hoards of aliens are trying to invade earth, or something equally cheesy.

When the game is varied to keep the player from getting bored (see sub-game variation) the story is also expanded; Perhaps the player defeated the first wave of aliens, but new (and more powerful) aliens have arrived. They look different and use more powerful weapons and tactics.

At some point, the story becomes prominent enough that players try to change the story through their gameplay. Perhaps they can kidnap the alien commander and stop the fighting now, or perhaps the war has all been a huge misunderstanding.

As soon as players want to change the story, which was originally added to simply answer "why", they are entering the realm of interactive fiction.

The transition to IF isn't binary. Some "games" can be 100% interactive fiction, while others are still games, but with a heavy emphasis on story.

Starting with interactive fiction, and adding games

Approaching the problem from the other direction, imagine beginning with a Choose Your Own Adventure, which is pure interactive fiction. (CYOA books have no games or puzzles.)

However, games and puzzles are easily added. They serve a few purposes:

Games and world-like worlds

Multiplayer games have another direction they can take:

1.         Instead of adding stories (interactive fiction) to games, multiplayer games can encourage players to compete.

2.         Competing players will team up.

3.         Teamed up players form social bonds.

4.         Players with social bonds tend to stay in the same game longer, 500+ hours.

5.         Players that stick with the same game tend to ask for new and varied features. Many (or most) of these features have social ramifications, emphasising social status and meeting other players.

These social features are commonly known to any Ultima Online player:

The more world-like a game gets, the more difficult it is to add interactive fiction components. While a world-like world may include quests, they're typically not central because (a) they're not needed by players who enjoy the world-like component, and (b) the players' ability to modify the world (with housing and politics) often conflicts with the author's pre-programmed IF content, and (c) creating an IF title that lasts 500 hours is simply too expensive.

Creation worlds

Clothing, player housing, in-game crafting and trade, and in-game government can take over the virtual world, forming a "creation world", like Second Life.

The fun of a creation world comes from creating new objects and seeing what other players have created, not from playing the game. Additionally, as developers expand the players' ability to create, they find that it unbalances the game to the point where the game is no longer playable, and it's discarded.

Creation worlds don't blend well with interactive fiction either; players simply have too much power.

A pattern? Genres?

These four types of worlds seem to form a pattern:

Interactive fiction

Game-like worlds

World-like worlds

Creation worlds

A "game" world can be heavily weighted towards one of the four types of worlds, or it can straddle two of them. Or, it can be centred on one genre, and steal a small bit from genres to the left and right; World of Warcraft is a game-like world that includes quests (tiny pieces of interactive fiction) and guilds (part of a world-like world's features).

It doesn't seem like a game can get much broader though; a world-like world cannot have interactive fiction in it (except in very limited form), and a creation world cannot be a game (although it can contain games in their own magic circle within the world).

Notice how the genres forms a linear sequence, going from "The author has all the power" to "Players have all the power."

Role-playing intensive worlds (RPI)

Some virtual worlds place a heavy emphasis on role playing. I'm not sure where they fit in relation to the other four genres of worlds. They can be based on game-like, world-like, and creation worlds, but they're often modified with extra features to encourage role playing. For example: If combat is supported in the game's "physics", it often has features that allow players to supersede the combat rules in order to maximise role-play.

 

 


 

Interactive fiction equation 2

(Back to TOC)

21 January 2007

by Mike Rozak

Discuss on www.mXac.net/forums

This is a second instalment to my Interactive fiction equation article. If you haven't read it yet, then do so now, or you'll be completely lost.

Note: Throughout this document I make a lot of simplifying assumptions about the equation and its terms. In the long run, many (or most) of these assumptions may prove to be wrong.

Summary

The final equation I came up with last time was:

E = Sd ( E(c) - P(y|d) A(c') - A(c, t) + ki ) I(d) P(d)

Writing a more-specific E(c)

In my last article, I glossed over the equation for E(c), and just provided a verbal explanation about how it described the enjoyment that a player got from the consequences of a choice, c.

Here's an actual equation for E(c):

E(c) = (Ec + kx (Ec - Exc)) · kw

The terms are:

In English, this equation says:

A player's enjoyment of a choice is affected by the consequences of the choice, Ec, what the player thought the consequences would be, Exc, and the player's personality, kx and kw.

The new E(c) presents some interesting (and obvious) conclusions:

Writing a more-specific A(c')

A(c') is a function that indicates how annoyed a player gets when their first choice isn't handled by the game. As with E(c), I didn't write an equation for A(c') in my previous article.

However, this time around, I have an equation:

A(c') = (kx (Exc' - Exc) · kw + ka) Xc'

The new terms are:

In English:

A player's annoyance at getting a "You can't do that" message for their first choice, c', is equal to the difference in enjoyment between the expected experience for the first (failed) choice, c', and the expected experience for the second (accepted) choice, c. This difference is kx (Exc' - Exc) · kw. Add an additional constant, ka, that players experience whenever they're told "You can't do that", and scale by the player's expectations of actually being able to act, Xc'.

Some interesting observations:

A new A(c, t)

A(c, t) was added to approximate how annoyed players got while watching a cut scene. Theoretically, it is based on a sum of A(c')'s caused when the player is watching the cutscene, gets annoyed with the direction it takes, and wants to make a choice, but can't. Every time a player wants to do something that the cutscene won't allow, another A(c') penalty is accumulated.

This relationship between A(c, t) and A(c') allows me to produce a slightly better cutscene fudge-factor:

A(c, t) = (kx (Cd · kw) + ka t) Xcs

The new terms are:

In English:

A player's annoyance with a cutscene is a function of how much the cutscene's expected enjoyment diverges from the cutscene's expected enjoyment if the player could make choices during the cutscene, kx (Cd · kw), and how long the player has to sit around an not be allowed to make choices, ka t.

Some observations:

The expanded equation

If I insert the new terms, I get:

E = Sd ( E(c) - P(y|d) A(c') - A(c, t) + ki ) I(d) P(d)

E = Sd (

(Ec + kx (Ec - Exc)) · kw

- P(y|d) (kx (Exc' - Exc) · kw + ka) Xc'

- (kx (Cd · kw) + ka t) Xcs

+ ki

) I(d) P(d)

E = Sd (

(Ec · kw) + kx (Ec · kw) - kx (Exc · kw)

- P(y|d) Xc' kx (Exc' · kw) + P(y|d) Xc' kx (Exc · kw) · kw - P(y|d) Xc' ka

- Xcs kx (Cd · kw) - Xcs ka t

+ ki

) I(d) P(d)

E = Sd (

Ec · kw

+ (kx Ec - kx Exc - P(y|d) Xc' kx Exc' + P(y|d) Xc' kx Exc - Xcs kx Cd) · kw

- P(y|d) Xc' ka - Xcs ka t

+ ki

) I(d) P(d)

E = Sd (

Ec · kw

+ kx (Ec - Exc - P(y|d) Xc' Exc' + P(y|d) Xc' Exc - Xcs Cd) · kw

- ka (P(y|d) Xc' + Xcs t)

+ ki

) I(d) P(d)

E = Sd (

Ec · kw

+ kx (Ec - Exc - Xc' P(y|d) (Exc' - Exc) - Xcs Cd) · kw

- ka (Xc' P(y|d) + Xcs t)

+ ki

) I(d) P(d)

In English:

The enjoyment from a game is the sum over all decisions of the following factors, all scaled by how important the player thinks the decision is, along with the probability of encountering the decision.

TV, movies, and books are just one long cutscene

Television, movies, and books can be thought of as a game with one long cutscene. With this assumption, the equation for linear fiction becomes:

E = Sd (

Ec · kw

+ kx (Ec - Exc - Xc' P(y|d) (Exc' - Exc) - Xcs Cd) · kw

- ka (Xc' P(y|d) + Xcs t)

+ ki

) I(d) P(d)

Xc' = Xcs, since Xc' can only go as low as Xcs. Basically, the player doesn't expect to make choices in linear fiction, but will still be slightly annoyed at the inability to do so.

I(d) = 1, for the sake of simplification.

P(d) = 1, since there's only one decision.

P(y|d) = 1, since any choices the player makes will always result in "You can't do that."

ki = 0, since there are no choices.

Cd = (Exc' - Exc), since the cutscene and the choice are the same.

E = (

Ec · kw

+ kx(Ec - Exc - Xcs 1.0 (Exc' - Exc) - Xcs (Exc' - Exc)) · kw

- ka (Xcs1.0 + Xcs t)

+ 0

) 1.0 1.0

E =

Ec · kw

+ kx ((Ec - Exc) - 2 Xcs (Exc' - Exc) ) · kw

- ka Xcs (1 + t)

Although this is a somewhat silly equation, it provides some useful conclusions:

An important observation, people that like TV, movies, and books will tend to have:

Real life

Using the same equation to describe real life, I get:

E = Sd (

Ec · kw

+ kx (Ec - Exc - Xc' P(y|d) (Exc' - Exc) - Xcs Cd) · kw

- ka (Xc' P(y|d) + Xcs t)

+ ki

) I(d) P(d)

P(y|d) = 0, since there are no artificial limitations.

Cd = 0, since real life doesn't have any non-interactive cutscenes.

t = 0, since real life doesn't have any non-interactive cutscenes.

E = Sd (

Ec · kw

+ kx(Ec - Exc - Xc' 0 (Exc' - Exc) - Xcs 0) · kw

- ka (Xc' 0 + Xcs 0)

+ ki

) I(d) P(d)

E = Sd (

Ec · kw

+ kx (Ec - Exc) · kw

+ ki

) I(d) P(d)

The equation says:

People that like real life have the following personality traits:

Gamers

People that enjoy real life, live in real life. People that enjoy TV (and movies and books) spend their time watching TV. The rest are often drawn to computer games, some of them to interactive fiction.

Therefore, a successful interactive fiction title/game should target the following personalities:

Boiling this down even further, hard-core game players:

It's nice to see that the interactive-fiction equation agrees with conventional wisdom! ;-)

 

 


 

Personal virtual worlds

(Back to TOC)

26 January 2007

by Mike Rozak

Discuss on www.mXac.net/forums

A few years ago, I had an E-mail discussion about personal virtual worlds (PVWs). The general idea is that if virtual worlds were easy (low-skilled) to create, and cheap to host, then millions of people would create their own virtual worlds, just like millions of people have their own blogs, MySpace pages, web sites, etc. PVWs would be a form of self-expression, just like blogs.

Personal virtual worlds "won't work"

I wasn't too keen on PVWs for the following reasons:

1.         Inevitably, all "development kits", including virtual world development kits, encounter a tradeoff between ease-of-authoring and flexibility. In order to make virtual world creation so easy that millions of people could create their own worlds, the toolkit would have to limit the variety of worlds that could be created.

In less abstract terms, a virtual world creation toolkit that's easy enough for millions of people to use will end up being a virtual dollhouse; players will be able to position stock objects around their world, and that's about it. No custom 3D objects, other than colours selected from a palette. And the stuff that really brings a world alive, scripting, will be non-existent because scripting is an uncommon skill.

2.         Virtual dollhouses might be fun for the authors to create, but they're not very entertaining for players, other than a quick look around to see what garish colour combination the author managed to invent. By the tenth such dollhouse, players will give up and never visit a virtual world created by "Easy-to-use virtual world creator" again.

3.         Chatting with other players might be fun. Unfortunately, with millions of worlds, players would be so thinly scattered that they wouldn't produce a critical mass. A player would log onto a world, see that it's empty, and immediately log out. One minute later, a different player would do the same. As far as the players are concerned, the millions of worlds might as well be single-player "games", but they're not even games; they're just empty chat rooms.

4.         With a slightly more complex toolkit, and fewer authors who could drive it, authors could create games instead of chat rooms. However, since the easy-to-use toolkit wouldn't require scripting, all the games would end up being exactly the same.

With only a hundred professionally-created MMORPGs in the world, an obvious and now-cliche Diku-game has already emerged. A million Diku PVWs would only be worse because they'd be exactly the same game, not just strikingly similar games. After the 10th Diku PVW, players would get bored and swear off playing in worlds created by "Easy-to-use Diku creator".

5.         Of course, the toolkit could allow for more complex gameplay and authoring choices via scripting, resulting in something like Neverwinter Nights (I or II). Instead of millions of dollhouse PVWs, however, the toolkit's complexity would limit the target market to only a few thousand authors. Unfortunately, NWN authors can't alter the fundamental game logic/programming, so the thousand worlds would still be very similar. NWN worlds do attract players though, even if the majority remain empty.

6.         Text MUDs go a step beyond NWN worlds; authors have access to the MUD source code (even more complicated than scripting) and can change everything about the game. Over the last fifteen years, www.mudconnect.com has amassed a list of 1700 MUDs, many of them defunct, and most of them player-less. And they're almost all clones based on Diku-MUD!

MUD clones exist because customising a MUD is too much work. Authors download the source code with accompanying "default" content of 2000+ rooms. They quickly realise that any change they can make to the MUD's 10 man-years of code and content is minimal, unless they're able to commit 10 man-years themselves. So, with no other choice, the defeated authors rename "Orcs" to "Sporks" and "Elves" to "Ethereal Beings", add a few hundred rooms to the 2000 stock rooms, and mis-label their MUD as "completely original" (since no one will try a MUD that's clearly marked as a "clone").

Some fundamental reasons why PVWs don't work

Let me rephrase some of what I stated above:

1.         It can be fun for an author to create a personal virtual world, even if all the author does is move virtual furniture around.

2.         Creating a virtual world that is interesting to players, however, requires 3D modelling skills, audio skills, and most importantly, programming skills. (Not to mention creativity, storytelling, and whatnot.) If an author doesn't have such skills then they can still have fun creating the world, but it's unlikely that players will stick around.

To play devil's advocate, I'll point out blogs, which as easy to write, attract readers: Millions of people write blogs, and at least 10,000 blogs are actually read. Blogs aren't difficult to write. They don't require any ultra-complex modellers or programming languages, but they still manage attract readers/players. Why shouldn't the same hold for PVW toolkits? Why can't a PVW toolkit be as easy to use as a word processor?

Blogs actually require enormous skill to write. The reason blogs are so easy to create (relative to PVWs) is that much of your education was devoted to learning how to write, not to mention all the conversations you have every day of your life. Very little, if any, of your education included 3D modelling, programming, and creating audio files. If you were lucky, you took a few art and music classes in elementary school. If your education emphasised 3D modelling and programming as much as it did writing, creating amateur virtual worlds would be a piece of cake.

Continuing the blog analogy: A virtual world development toolkit that doesn't require modelling and programming is like a blog server that only lets bloggers choose paragraphs out of a standard library; actually choosing individual sentences, or (God forbid!) individual words and forming unique sentences, is too complicated for illiterate people. (Notice that bloggers aren't forced to create their own fonts, though!)

3.         The more PVWs that exist, the lower the player density in the worlds. Low player densities turn the worlds into single-player games... that occasionally contain other players. If a PVW's design assumes that other players create most of the fun, then an empty PVW won't be much fun, which creates a self-reinforcing feedback loop when players log on, see no one around, and then immediately log out.

4.         Text MUDs, in particular, try to be enormous, some claiming to be as large as 20,000 rooms. I suppose that larger worlds attract more players to try the world. However, huge worlds are problematic:

o    They reduce the player density still further.

o    They take more time for players to complete, so they're not accessible to the 75% of the population that works, and/or has children, and/or has a life.

o    An emphasis on "huge" causes authors to reduce quality in favour of quantity... but quantity isn't needed by PVWs because there will hypothetically be thousands (or millions) of them, most of dubious quality merely because their authors aren't skilled enough. Inducing a further decline in quality by encouraging quantity is merely another nail in PVWs' coffin.

5.         Players' expectations are high. After having played in a world that took 300 man-years to create, a text MUD that took 10 man-years is, for the most part, unappealing.

Beginning with a 10 man-year text MUD as a base, an amateur author could hypothetically devote one man-year to changing and customising the code and content, and numerically create an experience that is 10% different from the original code/content. Users would perceive the difference between the new MUD and its ancestor to be greater than 10%, though. I guesstimate that a 10% real change will be perceived as a 20% difference. 20% is on the threshold of being different-enough that players who played the original MUD will want to try the modified one. (The exact number isn't important here; You can come up whatever scaling and threshold makes sense to you.)

However, only 50,000 - 200,000 people play text MUDs; There isn't enough eye candy to attract a larger audience. 20 million (to use a round number) play 300 man-year eye-candy-laden MMORPGs.

If a player suddenly got a hold of WoW's source code and models, and devoted a man-year to customising it, the game would still be 99.7% WoW! Even doubling this value to simulate how the changes are perceived, after one year's work, the game would be perceived as 99.4% WoW... which means that the experience would be almost exactly the same. To create a "new" experience from a WoW base, a team would require 30 man-years (10% of 300) of customisation... hardly an amateur endeavour.

The solution lies in stating the problem

If I invert the problems, a possible solution reveals itself:

1.         Create a virtual world toolkit that encourages quality over quantity. If the world takes more than six hours to play through, it's probably too long.

Side note: The more years I spend thinking about amateur virtual worlds, the shorter I think they should be. A few years ago I anticipated a 50-hour MMORPG, like GuidWars, in my anti-MMORPG writeup. I now think that even 50 hours is too long.

2.         The toolkit should encourage authors to create a fun single-player game (or experience), such as an interactive fiction, CRPG, or FPS. If other players happen to show up, they're an added bonus to the fun, but their presence can't be required.

3.         The toolkit must emphasise customisation. For the most part, the game code and graphics should all be replaceable, including fundamental assumptions such as gravity.

4.         Here's a tricky (and contentious) solution: The toolkit should only be a few man-years of work, or rather, the parts important to a player's perception of what makes a world different should only be a few man-years. If not, any attempt that authors make to customise the experience will be overwhelmed by the mass of pre-existing code/content, as in Diku-MUD clones that are all similar because authors don't have the manpower to significantly change them.

I suspect that there are ways to circumvent this limitation, to an extent, such as allowing third party models and code to be bolted in by authors. However, for this to work, authors must have a large selection of models and code, and the models and code must themselves be customizable.

5.         Include all of the necessary tools in the toolkit, and perhaps even offer hosting. Requiring the author to download the toolkit, then also install a separate 3D package, database, scripting language, audio editor, etc. isn't good enough. There's no reason to make things more difficult than they need to be.

This is only one solution. There are others:

1.         Create a dollhouse PVW toolkit that's designed to be fun and easy for everyone to use, but where no one seriously expects players (other than the author's friends) to show up.

2.         Create an amateur PVW toolkit, like I described above.

3.         Create a professional virtual-world toolkit that is used to create worlds in the top-100 list.

Attracting players to amateur PVWs

What attracts players to an amateur PVWs?

Postscript

I thought of an alterative name for PVW's... "Mini-MMORPGs" or "Micro-MMORPGs"... which would be abbreviated as "MMMORPG"! I decided that PVWs sounded better. :-)

 

 


 

Topographies

(Back to TOC)

18 March 2006

by Mike Rozak

Discuss on www.mXac.net/forums

People usually associate "topography" with geography, but games can include a variety of topographies.

Alternate realities

Some avatar games include alternate realities:

Random observations:

Geographic spaces

Several different geographic spaces have been explored in avatar games:

Random observations:

Social spaces

When I have said, "NPCs are the game!", here is part of what I meant:

Random observations:

Of course, the NPCs exist within geographic space.

Idea spaces

Ideas (and knowledge) have their own space too:

Random observations:

Ideas exist within NPC social-space. As per Chris Crawford's work, ideas and knowledge can flow through social space, affecting characters.

 

 


 

Multiplayer interactive fiction

(Back to TOC)

18 March 2006

by Mike Rozak

Discuss on www.mXac.net/forums

In this article I will explain what (I think) "multiplayer interactive fiction" (MIF) is, and how it differs from other game genres.

A definition of multiplayer interactive fiction

Multiplayer interactive fiction is an online computer "game" which:

1.         Lets you play a character in a virtual world. The world isn't limited a standard fantasy or science fiction setting, and might take place in Victorian England, modern-day New York City, or even a dream world.

2.         The world is centred around a story that your character participates in, such as a detective solving crimes in Victorian England, a romance in New York City, or the more familiar "save the world" fantasy and science fiction stories.

3.         Your actions affect the story's direction, although this is limited by current technology and how much time the author had to write in narrative choices. You can even forgo the story altogether and follow your own muses, although the game experience may not be as rich.

4.         The world is filled with sub-games. IF titles aren't limited to the standard combat sub-game (common to computer role-playing games) and puzzle sub-game (common to adventure games). Sub-games, which are customised to the story, might involve interrogating non-player characters, romancing them, or trying to run a property-rental empire.

5.         In (multiplayer) interactive fiction, sub-games are used to enhance the story, much as music and special effects are used to enhance a movie's story... instead of the movie's story merely being a vehicle to show off music and special effects.

6.         Much of an IF title's experience comes from the "scenery". You might find yourself spending much of your time in activities that have nothing to do with the sub-games, such as listening non-player characters' anecdotes, exploring the world, or chatting with other players, much like you'd do on a real-life vacation.

7.         Many (but not all) IF titles are multiplayer. You can team up with other players, just sit around and chat, or stick to yourself. Sometimes you'll even compete against them.

8.         (Multiplayer) interactive fiction titles tend to be short, requiring between two to ten hours to complete. When you finish one, visit the CircumReality web page and download a link to another. (Or for those who like a challenge, write your own.)

9.         Most IF titles are created by hobbyists, who author the titles for the fun of it, just like people write blogs. If you enjoy a title, or even if you find some problems with it, make sure to tell the author what you thought; They like to hear that players are enjoying their works of art, and are always interested in improving their creations. Also, if you enjoy playing an IF title, make sure to tell your friends. Being hobbyists, authors don't have an advertising budget!

10.      IF titles that use CircumReality will tend to be slow-paced and intellectual due to the way CircumReality uses still scenes and spoken narration.

Elements of multiplayer interactive fiction

Since the previous definition was probably insufficient, let me cover the elements of MIF:

Comparison to other genres

To understand how MIF differs from other genres, I'll first explain how other genres use the above elements to create gameplay:

What players do... (a 30,000 ft. view)

Here's a high level view of what players do in MIF:

MIF's core gameplay loop

In, The game loop, I described a loop of actions that seems to be common to every avatar game. For example: In a CRPG, the game loop is to kill small monsters, to get treasure and experience points, to enable the character to kill even bigger monsters. In an adventure game, the game loop is to solve puzzles, to enable the player access to other parts of the world, allowing them to solve even more puzzles.

In MIF, players wander around a world filled with hundreds (perhaps thousands) of NPCs. The core gameplay experience comes from interacting with the NPCs, mostly by talking to them, although combat and other forms of interaction are possible. In simplistic terms, the game loop is:

1.         Players encounters a NPC somewhere in the world.

2.         They figure out how to befriend (or defeat) the NPC.

3.         Players then act on their plan and try to befriend (or defeat) the NPC.

4.         Once friendly (or defeated), a NPC may provide material assistance, information, or contacts that help the player befriend (or defeat) more NPCs and/or access other parts of the world.

5.         Repeat until all the content is used.

What players do... befriending NPCs

In MIF, non-player characters are (relatively) complex AIs. They have personalities, relationships with other NPCs, and opinions about the player character (as well as other NPCs). Just as with real people, every NPC has a different personality.

To befriend a particular NPC, the player must first figure out "what makes that NPC tick"...

Once a player learns what makes a NPC "tick", they need to act on this. Such action might include:

Once friendly, what does the NPC do for the the player?

Player vs. player

I'm not sure if I want to create a player-vs.-player game, but the same core gameplay could be used for player-vs.-player interactions:

One problem with including player-vs.-player is that it encourages players to use game walkthroughs in order to get as influential with NPCs as possible. Once players start using walkthroughs, puzzles have to be removed from gameplay because they're trivialised by walkthroughs. This leaves only "grind"-like gameplay, a condition that I'd rather avoid.

Story

I hate to use the term "story" because it's so overloaded with meaning. Having said that, one important way that MIF differs from other games is that the world is filled with stories:

1.         Players can partake in larger pre-programmed "story", such as defeating the evil emperor, figuring out who the murderer is, cleaning up the city so it wins the "Tidy town" award (as a sanitation engineer), etc. This is nothing new, since adventure games and CRPGs always include this form of "story".

2.         Players can become secondary characters in "stories" between NPCs. A player might help two lovers elope, then help later when their child becomes ill. While many adventure games and CRPGs incorporate such stories, MIF will make greater use of them.

3.         NPCs have their own stories, regardless of what the player does. While talking to the village gossip, players might hear short tales about a villager that just won the lottery, or one that is having an affair... which might prove to be a useful skeleton to bring up in conversation with another NPC.

4.         Being a multiplayer game, players will generate their own gossip (and stories) about what other players are doing.

What players do... Sample gameplay

Since gameplay might still be a little unclear, I thought I'd illustrate some sample gameplay.

The scenario is one in which an old woman, the local town gossip, knows a salacious rumour that the player needs in order to boot the town mayor out of office. Unfortunately, the woman doesn't trust the player at first, and isn't too eager to give up her prize rumour. She is, however, an avid collector of snow globes.

To demonstrate the gameplay, I'll also explain how the scenario would be implemented for adventure games and CRPGs, so you can see the difference.

Note: This scenario isn't the only way that multiplayer interactive fiction can play out. It's just pointing out how the experience can be very different from an adventure game or CRPG/MMORPG. Multiplayer interactive fiction often incorporates elements from CRPGs, MMORPGs, and adventure games, and might require a few goblin kings to be slain too.

Usurping the term "Interactive fiction"

The term, "interactive fiction" (IF), was coined in the 1980's to describe the genre of game that Infocom were creating. Over the past 25 years, Infocom has died and professional "interactive fiction" has been superseded by "(action) adventure games", but the term, "interactive fiction", continues to be used by a small group of dedicated hobbyists.

People who write interactive fiction consider their work to be different than adventure games and CRPGs for the following reasons:

I have adopted the term "multiplayer interactive fiction" (MIF) even though the beast that I've created doesn't fall neatly into the traditional interactive fiction definition:

 

 


 

Nutritional game design

(Back to TOC)

1 January 2008

by Mike Rozak

Discuss on www.mXac.net/forums

I have lately been trying to create content for my game, CircumReality, but content design has been "slow as molasses" because of a mental block I've had; I haven't been happy with my intended content, and I wasn't sure why.

A few nights ago, I had a dream. While wandering around the dream and doing dreamy stuff, I thought to myself, "Why isn't my game as fun as my dreams?" I have since tried to answer that question, and this writeup is my answer:

Cravings

Have you ever noticed that after a hot, sweaty day that you have cravings for salt? Actually, you probably don't think of salt directly, but you want potato chips or some other very salty food. That's because your body is low on salt, and it uses cravings to get you to eat something salty.

Players play games because they have mental cravings. They're looking for some sort of experience, feeling, or emotion that they know can be provided by the game.

For example: Some players like MMORPG "crafting", whose "experiential" ingredients/nutrients are gathering (collecting materials), meeting new people (to buy/sell goods to), a challenge (to get the highest price), and changing the world (as crafters see customers using their equipment).

According to my "cravings" theory, someone that likes crafting should like other activities (sub-games) so long as they provide the same basic nutrients.

I know this isn't entirely true with food, so don't expect it to hold for gameplay cravings: Chocolate chip cookies are made from flour, sugar, butter, eggs, and chocolate. If I ask for a chocolate chip cookie and you give me the raw ingredients, I won't be happy. Nor will I be entirely satisfied if you mix them up in a different configuration, such as a chocolate cake... although I won't complain that much. With familiarity, I might even prefer the chocolate cake to chocolate chip cookies.

The whole is more than the sum of its parts, and any activity is more than the sum of its experiential ingredients/nutrients. Despite that, understanding what ingredients/nutrients make up an experience is a valuable tool for game design because is it predicts what activities a player might like, and what activities can be substituted for one another, allowing MMORPGs to escape from the "Kill ten rats" cliches.

Here's an incomplete list of some of the ingredients/nutrients that players get from game activities (sub-games):

Activities (sub-games) in a game need to support one or more of these ingredients/nutrients, the more the better. For example:

If a game offers activities that provide players with experiential ingredients/nutrients that players crave, players will naturally gravitate towards whatever activity meets their needs. In other words, if a game offers a buffet, people will pick and choose as is best for them, which sounds reasonable to me, and happens to be the way most MMORPGs are organized.

I used to oppose offering MMORPG activities "buffet style", but I have changed my mind (somewhat). However, I still feel that there should be limits to the buffet:

Eat your Brussels sprouts!

Players don't always partake in the activities that are "good for them". A few reasons exist:

Several techniques (aka: spices and artificial flavours) can encourage a player to try the activity, or to "make an activity more fun":

Avatar games (such as MMORPGs, CRPGs, adventure games, and first-person shooters) are not stories, but stories frequently use several of the above-mentioned techniques. (Avatar games aren't really games either.) The most common is to create a character that readers become "friends" with, such as Harry Potter, and then put him in danger, adding a touch of mystery. It's a proven formula to get readers to read "just one more chapter" even though they'd rather be going to sleep.

Use these techniques sparingly because they're a finite resource!

Use these techniques to get players to eat their Brussels sprouts.

Conversely:

Here's an anecdote: I created a hobbit character in Lord of the Rings Online. The Shire had oodles of fed-ex quests. Some of them were sub-games, involving time limits and NPCs that would "catch you" and abscond with the sweets you were carrying. They came with rewards, which was good, because I wouldn't have played the moderately-fun sub-games without the offer. However, most fed-ex quests had no sub-game component, but they still included rewards. I found myself crisscrossing the shire dozens of time delivering messages and packages, something that I found quite boring. But, I was rewarded for it, so I did it. And I was rewarded for killing rats (or whatever they were called), so I didn't kill any rats along the roadside until I was given a quest to kill them. In fact, I didn't do anything unless I had a quest for it because there was always a quest for everything, and quests always provided better rewards. Ultimately, I found myself being led around "by the nose", feeling like I didn't have any choice in the matter. The world conceptually changed from a beautiful landscape populated by (boring) NPCs and monsters into a task list.

If killing rats is a "fun" activity by itself and players know that, then there's no reason for NPCs to offer rewards to kill rats. A NPC should still point out the rats so that players know where to find them, and so that the rats' existence is explained for the sake of the world's realism, but the NPC doesn't need to offer anything extra. Rats might still provide experience points as a way to introduce new gameplay so that players don't get bored, and/or loot to synergise with the trading/crafting game.

A typical contemporary MMORPG has the NPC offer a reward of money or loot, and experience points, in addition to whatever the rats provide. This turns the MMORPG into New York City, where tips are expected for everything, which also means that like NYC, tips no longer provide any incentive to provide good service. The act of always providing activity-external rewards means that activity-external rewards can't be occasionally used by game designers to encourage players to "eat their Brussels sprouts".

To play devil's advocate, having NPCs offer rewards for killing rats solves a few problems:

Family restaurants

As I've written about before, there's a reason why most restaurants are family restaurants. Family restaurants provide a varied menu so that everyone in the family can find something they like. The reason they're popular is because every family member has veto powers, and any restaurant which is disliked by even one of the family members is struck from the list. Only those restaurants that don't get vetoes survive.

The same is true for MMORPGs. A mass-market MMORPG must cater to all players' mental cravings, not just a few specialised ones. Any MMORPG that specialises has exponentially fewer players.

Games are memes

I wasn't sure whether to put this at the beginning or the end of the article since it belongs in both places.

People won't play your game unless they know about it and hear that it's good. (The same goes for restaurants.) In order, players are more likely to try a game (or visit a restaurant) if:

1.         They first play the game (or visit the restaurant) with friends.

2.         They have a recommendation from friends, but play/visit alone.

3.         They have a recommendation from reviewers.. who, due to legal and commercial threats, rarely give an clear opinion about the experience.

4.         They see an ad.

5.         They happen to like the look of the game's box when they see it at the store (or happen to like the look or name of the restaurant).

In other words:

1.         If my friends are enjoying a MMORPG, they'll tell me about it too, since playing a MMORPG with friends makes it more enjoyable. This is an incredibly strong positive (or negative) feedback cycle, and explains why mass-market MMORPGs must be family restaurants.

2.         If I ask my friends for a good MMORPG (or game) to play, they'll tell me about the one which made the biggest "impression" on them. This is where "eat your Brussels sprouts" comes in. The reason that "classic novels" (which include plenty of Brussels sprouts) have stayed around so long is because they made a lasting impression on people (partly due to the Brussels sprouts) that causes readers to remember and recommend the books to other people (aka: a meme, a viral idea). Airport novels, on the other hand, don't say anything meaningful and are quickly forgotten, never to be recommended, and soon out of print.

3.         If your game can't rely on 1 or 2, then you'll need a really good PR team and a huge marketing budget.

Conclusion

Getting back to my dream. Why was it more fun than my in-progress game? Because my dream satiated some of my mental cravings and included some deeper meaning ("Brussels sprouts"), neither of which are being implemented properly by my current content... but I'll change that now.

Some of the changes are:

 

 


 

Making players forget they're playing a game

(Back to TOC)

22 March 2008 (Revised 27 Mach 2008)

by Mike Rozak

Discuss on www.mXac.net/forums

As I stated in Nutritional game design, players play games because games provide something they want (or need). For example: A player may wish to play a game because he wishes to be Sir Lancelot.

However, there's a problem: While playing the game (of being Sir Lancelot), it is blatantly obvious to players that they are merely playing a game, and that they're not really Sir Lancelot. The reality is that they're sitting in front of a 19" monitor, pressing 'A', 'W', 'S', and 'D' to move, 'space' to attack, and '1' through '4' to speak different sentences to obviously artificial characters. Somehow, 'space' is supposed to mimic the thrill and excitement of a real sword swing, and '1' through '4' the attacks in a verbal battle of wits.

Just like readers of a novel, game players must willingly suspend their disbelief.

Unfortunately for the player:

The more that a game calls attention to its nature (by using a 'space' button to swing a sword, as opposed to a real sword with motion sensors), the less able the player is to fulfil his dream of being Sir Lancelot!

To flip this statement around:

Immersion is necessary for a game's activities to fulfil a player's "nutritional" needs.

To some extent, a player must forget that they're playing a game for the game to work.

Game designers know this, and employ a number of techniques to immerse players:

Some hybrid techniques are also used:

If immersion is required for a game's activities to fulfil a players' needs (which is why they're playing the game), then some conclusions come to mind:

 

 


 

A rose-filled world

(Back to TOC)

1 May 2008

by Mike Rozak

Discuss on www.mXac.net/forums

When I first started thinking about writing a game, I considered the following “game” design:

Imagine wandering around a world and just watching – “smelling the roses”, so to speak. A player could wander around, watch what the inhabitants were doing, and be entertained by just viewing, just as a story entertains readers, who are merely watchers (not doers).

Theoretically, players wouldn’t be able to affect the world at all.

Except, of course, the design wouldn’t let players miss the important action. Scenes would be frozen until the player was there to observe them.

And players would be able to talk to NPCs, and maybe even interact by playing cards with them, or partake in other sub-games that wouldn’t interfere with the world/story.

So, the player would affect the world somewhat, but only on the margins, like deciding which shade of black to paint their Model-T Ford. (Henry Ford is attributed with the quote, “The customer can have any colour he wants so long as it’s black.”)

As I said, I was originally going to write such a “game”, but decided not to. I didn’t have any firm, logical reasoning for not wanting to; it just didn’t feel right. For years now, I’ve been wondering if the idea would “work”.

It won’t work.

The reasons are:

1.         Games are about choice. Without choice, players might as well read a book or watch a movie.

2.         Choices without consequence (change/agency) aren’t valid choices. If you’re given a choice of what’s behind door A or what’s behind door B, and later discover that the exact same thing was behind both, you’ll be miffed. (See Choices III.)

3.         Without choice, players would feel like ghosts, unable to actually do anything. Most would realize this and leave unsatisfied. Those that remained would be tormented souls, spending eternity wandering the world, rattling their virtual chains.

But, you might say, typical MMORPGs are exactly that, except the NPCs hardly exist and those that do don’t actually do anything. MMORPGs are exactly what I described except (a) there are very few roses to smell, and (b) character advancement is king.

It still won’t work. (And typical MMORPGs don’t work either; I’ll explain later.)

I’ll expand my “smell-the-roses” world to include typical MMORPG features, though still without world-changing agency:

1.         Players can walk around and smell the roses.

2.         The roses make sure not to actually bloom until players are around to see and smell them.

3.         Players aren’t limited to smelling the roses; they can also touch them.

4.         Players can partake in rose-like activities, such as flower arranging, playing poker with rose-themed cards, and/or killing rose-monsters.

5.         Players can smell the roses together.

6.         Players can collect roses of different varieties and colours. These collections allow players to ultimately collect bigger and better roses from more dangerous parts of the world.

7.         Roses can be used to affect (prick?) other player characters, and indirectly affect other players.

What do players think of this kind of world?

To simplify things to a controversial statement:

MMORPG fundamental problem #1: MMORPGs are fundamentally broken because players can’t change the world.

Even though gameplay involves millions of choices, players eventually realize that all those choices have only two effects: (a) advancing their characters, and/or (b) affecting their relationships with other players.

Thus, the types of players attracted to MMORPGs are (a) people whole like advancing their characters (achievers), and (b) people who like hanging around people who like advancing their characters (socialisers). A few killers come along for the ride, because they like having people around that they can dominate. And a few explorers find the paucity of roses (explorable bits) present in a MMORPG enough to keep them interested.

I’ve been lying (somewhat). Some MMORPGs have limited world-affecting agency:

However, these change-powers aren’t enough, for two reasons.

MMORPG fundamental problem #2: MMORPGs are fundamentally broken because MMORPGs where players can change the world (often by owning something) all tend provide the same “you can change the world in such-and-such a way” feature.

Face it, almost every MMORPG has housing. It’s nearly as cliché as orc whacking. Players have already owned MMORPG houses before, so being able to own more housing isn’t seen as terribly desirable. Vehicle ownership is the new housing (housing was new for MMORPGs circa 2000). Personal NPCs will be next.

What players can change about the world needs to be a unique selling point!

The other fundamental problem is much scarier for MMORPGs:

MMORPG fundamental problem #3: MMORPGs are fundamentally broken because when players do change the world, it will eventually revert back.

It must; letting a sieged castle stay occupied by a guild forever, for example, means that new players will never get to siege that castle, instantly “wasting” all the time and effort that went into the castle’s design. Even worse, if a group of players were to actually defeat the evil overlord, a whole new one would have to be invented, including all the quests that lead to the final defeat.

The only solution to this problem is to “rush the players out the door” soon after they’ve changed the world in the largest and most meaningful way. Failure to do so shatters the illusion of change, and quickly negates any solutions to problems #1 and #2.

MMORPGs don’t rush the player out the door because their business model relies on keeping players around so they keep paying. Consequently, players eventually learn that all changes are ephemeral, except (you guessed it) changes to their character or their relationships with other players. (...as well as whinging on the forums and harassing other players off the game.)

So what does a virtual world that allows change look like?

A word about MMORPG business models, since the problems I described represent classic paradigm shift conditions:

 

 


 

Immersion-emotion feedback loop

(Back to TOC)

25 June 2008

by Mike Rozak

Discuss on www.mXac.net/forums

A few years ago, I read the book, Creating Emotions in Games, by David Freeman. I didn't entirely agree with the book. Two main messages from the book rubbed me the wrong way:

  1. People play games, watch TV/movies, and read books to be taken on an emotional rollercoaster ride. I don't quite agree with this statement, but I have a theory about why emotions are important though. See below.
  2. The examples from the book imply that an "emotional" game should be an emotional cut-scene, followed by FPS or platformer gameplay, followed by another emotional cut-scene, then more FPS/platformer gameplay, etc. I definitely disagree with this. I suspect David Freeman was intentionally using common/cliche FPS/platformer gameplay as an example. Or at least I hope he was.

As I pointed out in Making players forget that they're playing a game, an important feedback loop between immersion and emotion exists. The two are intertwined.

  1. Games do what they can to encourage immersion, like including eye candy, large worlds, gameplay, etc.
  2. Immersion allows players to get "emotional" about situations within the game.
  3. Emotional situations, in turn, cause immersion.
  4. More immersion means stronger emotions, which means more immersion.

The same loop happens in TV/Movies and books. An excellent example is the classic movie, Cat on a Hot Tin Roof, which gradually draws the viewer into a troubled family. It begins with mystery, and rollercoaster's its way through a plethora of emotions.

Emotion is critical. Emotion is critical. Emotion is critical!

It's so important that I'd go as far as saying that:

One more thing...

Many "How to write a story" books say that "story = conflict" and that it's impossible to write a good story without conflict. I'll rephrase that equation: "Story requires immersion. Emotion encourages immersion. Conflict encourages emotion. Thus, conflict encourages story." Don't mistake the symptoms of the disease for the disease itself!

Likewise, many people assume that "computer game = gameplay". In other words, everyone assumes that a "computer game" (for lack of a better term) must include gameplay. Gameplay is the technical definition of opponents, choices, winners and losers, etc.

As with stories, don't mistake the symptoms for the disease:

"Computer games" require immersion. Gameplay encourages immersion. Gameplay also encourages emotion. Thus, gameplay is a useful device in making immersive "computer games".

The reason why gameplay is often an integral part of "computer games" is because:

At this point, I could go a step further an claim that the traditional Bartle player types (achiever, killers, explorers, socializers) have emotional correlations too, but I still need to think about it.

 

 


 

Choices, part 4

(Back to TOC)

25 June 2008

by Mike Rozak

Discuss on www.mXac.net/forums

Before you read this article, you should familiarize yourself with Choices, part 3.

Computer games (and gameplay) require choice. Player's choices must have consequences. This article discusses consequences.

The consequences of a choice can be categorized in two ways:

Thus, for every choice a player makes, a designer can create a table of the consequences. This table for "walking forward" might be:

Expected
and happened

Expected
but didn't happen

Unexpected
with feedback

Unexpected
without feedback

Positive

Character moved forward, which aids in completeing the goal, "Walk towards the castle."

Neutral

The grass is flattened where the player walks.

The garden-gnome is annoyed by the player walking on the grass, and never talks to the player.

Negative

The character was blocked after a few steps by an invisible wall.

A 16-ton weight drops on the character's head.

Why is this table useful? (Here are some random bullet points that explain "why".)

Creating a table of different consequences hints at the different types of effects for consequences. Some of them are:

 

 


 

Why allow players choice?

(Back to TOC)

19 September 2008

by Mike Rozak

Discuss on www.mXac.net/forums

Since I began my interactive fiction "game" project, I've been asking myself over and over: Why do I want to make an interactive experience? Why not just write a story?

My initial answer was:

While this reason is correct, it's incomplete.

Interactivity comes at a cost. It introduces all sorts of problems that non-interactive entertainment (stories) doesn't have:

These are enormous problems, which make me wonder why I even bother trying to write interactive entertainment.

Interactive entertainment has its advantages though:

So what's the moral of the story?

 

 


 

Fallout analysis (the recipe)

(Back to TOC)

27 November 2008

by Mike Rozak

Discuss on www.mXac.net/forums

As anyone who reads my write-ups knows, I like looking at the elephant from every angle (see the blind men and an elephant anecdote, http://en.wikipedia.org/wiki/Blind_Men_and_an_Elephant), so to speak. I keep coming up with different theories (angles to look from, or parts of the elephant to feel out), and testing them against successful games.

Recently, I’ve been playing Fallout 3 for fun… and at the same time, seeing how well my theories match what Fallout is doing design-wise. (For all those Fallout/Oblivion bashers: Yes, Fallout has many flaws, but I don’t expect perfection and can look beyond the flaws.).

Fallout is “fun” to play because it does a decent job of meeting a few basic requirements:

Making a world that’s interesting and relevant to the player

If players could use a device to teleport them to the alternate reality of Fallout (yet not be subject to injury by the alternate reality – radiation, bullets, etc.), what would make the alternate-reality Fallout “fun” for players?

o Escapism – From the escapist’s POV, Fallout is fun simply because it isn’t the real world… and it isn’t yet another cliché Tolkien-based world either.

o Identity experimentation – Players can experiment with being evil (or good).

o Growing up – For teenagers, the game is about growing up: Freedom, learning, becoming more powerful, and making your own choices.

o Fallout answers the question, “What is it like to live in a post-apocalyptic world?” (I suspect people were more concerned with this question in the 1950’s than the 2000’s.)

o MemeletsMemelets are small ideas that are scattered throughout the game world, such as observations about the sanitized 1950’s culture, thoughts about the fragmentation of society, human behavior under difficult circumstances, etc.

o Mystery – The world is filled with conspiracies and unknowns, attractive to many players.

o Combat – Excitement, frustration, success.

o Stories that tug on emotions (through quests and NPCs) – Better NPC and story design could have produced a greater emotional impact.

o Satisfaction when quests/goals are completed

o A sense of wonder from the scenery and memelets scattered throughout the world.

o NPCs – If players can be made to care about some of the NPCs, then players care what happens to the NPCs, and will “fight” the rest of the world to help the NPCs.

The recipe

In other words, here’s the generic recipe that Fallout and other CRPGs follow:

Games don’t need to follow this recipe exactly, but (I suspect) leaving out ingredients will result in a less-successful game.

Notice how Richard Bartle’s explorers, socializers, killers, and achievers appear. According to this theory, player types are people who prefer one ingredient in the recipe more than others… kind of like chocoholics or sweet-tooths.

The events coordinator

Any good holiday resort has an event coordinator, who is tasked with making the visitors’ experiences enjoyable and memorable. Partly, the event coordinator makes reality more fun than it normally world be.

The event coordinator also keeps the tourist/player busy so they don’t get bored and start whinging about how rainy it is, or how the local bakeries don’t make pasties like they do at home. Fallout keeps the player’s brain (and virtual body) constantly occupied so that players don’t notice they’re not a real world:

o Lots of visual stimulation (eye candy), and motion

o Acoustic stimulation (sound)

o Language center stimulation – Not only do NPCs speak, but a radio is playing in the background.

o Movement requires that the player concentrates so they don’t get lost

o Traps further encourage players to pay attention

o Monsters also require concentration

o Players must keep an eye out for resources (like ammunition)

o Puzzles bar the way – Although limited in Fallout

o Players must remember and recall the layout of the land.

o NPCs and their relationships with other NPCs must be remembered

o Players must remember what objects and monsters do

o Memelets are scattered around

o Players must learn and master combat techniques

o Quests and goals – In Fallout, quests handed out by NPCs and the radio station.

o Players must decide which resources they will need in the future.

Note: The concentration, memory and learning, and planning aspects tie in with Raph Koster’s “A Theory of Fun”.

Making the virtual world feel like a real world

What if the hypothetical alternate reality (step through a machine and get to the alternate reality of Fallout) were more of a Disneyland-like theme park in the real world? The world would still be in real, but located on real-life Earth, populated by robot monsters, and run by real actors, all for the benefit of players. What would the requirements be for a “fun” world?

· All of the above.

· The props department would have to manufacture realistic costumes, monster robots, and sets… also known as “eye candy

· An Earth-sized Fallout-land wouldn’t be possible. Fallout-land would still need to be fairly large though, with plenty of manufactured detail. A few thousand acres might suffice, large enough that boundaries aren’t easily encountered. The world would need to be detailed enough that it looked “real” to a casual observer.

· NPCs (actors) would need to wander around the world and ostensibly live their own lives, pretending that the world is real whenever players are watching.

· Players must be able to change the world (to an extent) with their actions.

Please ignore the man behind the curtain

What issues would occur if the Disneyland version of Fallout were converted into a virtual reality simulation (aka: computer game)?

As a game, Fallout tries to NOT remind players that they’re playing a game:

o The “most important parts” of reality are simulated, such as the 3D, the ability to move around, etc. (Eye candy)

o NPC AI is critical. If quality AI isn’t achievable (which it isn’t), then the world needs to have fewer NPCs, or use NPCs in ways that don’t require much AI (such as combat).

o Fallout employs realistic-looking graphics (although a bit to grey for my tastes)

o Fallout’s NPC speech is audio, not just text scrolling across the screen

o The screen is completely filled with world-simulating 3D graphics.

§ Displays like the compass and player hit points have minimal visual impact.

o Even when stats and maps are pulled up (on the Pip-Boy), they’re made to look like an in-game object.

§ Note: Thinking about how Fallout minimizes clutter on the screen caused me to change my game UI. I now hide the auto-map until players specifically ask for it, and even then it only appears temporarily. After making these changes, I immediately noticed an increase in my game’s “immersion”.

Semi-relevant random thoughts

Random thought #1:

Many of the design goals are in conflict.

For example: Making the control mechanism simpler (increasing immersion) reduces the number of choices that players can make, which makes the virtual world feel less real (reducing immersion).

For example: Allowing players to make choices (increasing immersion) reduces the quality of eye candy that’s available (reducing immersion), and vice versa.

Game designers must make tradeoffs between the many conflicts.

Random thought #2:

In any game, eye candy can be improved by using more cut scenes, or by turning the game into a Choose Your Own Adventure (CYOA) experience. However, CYOA reduces the player’s choices, which counteract much of the “immersion” produced by providing the eye candy.

Historically, CYOA has only been successful when the eye candy provided by the CYOA experience has been MUCH better than the eye-candy provided by the contemporary game-like (procedural) experience.

For example:

· CYOA and Fighting Fantasy books – They eye candy was only verbal (see below). There were no alternative game-like experiences to compete with, so CYOA and Fighting Fantasy books sold well.

· Dragon’s lair – The eye-candy was cell-animated, stored on laser disc. The alternative procedural games were Robotron and Pac Man, both with poor eye candy.

· Phantasmagoria – The eye-candy was pre-rendered 3D graphics combined with video of live actors. The alternative procedural games had sprite-based characters, or extremely primitive 3D characters, both inferior to the CYOA-based Phantasmagoria eye candy.

As far as I know, those are the only successful CYOA “games”. Since Phantasmagoria’s success in 1996, 3D accelerators have improved enough that no subsequent CYOA has been able to produce vastly-superior eye-candy. Hence, no CYOA since 1996 has been successful.

Random thought #3:

Why do books “work” if don’t have any eye candy?

Two reasons: (a) We’re trained from an early age (and perhaps even genetically enabled) to use words as substitutes for realistic visuals. (b) Writing is the “eye candy”, or at least good writing is.

To illustrate: Think of a paragraph from a good writer (like Dickens) and then consider that same paragraph as it would be displayed in a text MUD/IF, procedurally generated from smaller text snippets.

Dickens:

The evening arrived; the boys took their places. The master, in his cook's uniform, stationed himself at the copper; his pauper assistants ranged themselves behind him; the gruel was served out; and a long grace was said over the short commons. The gruel disappeared; the boys whispered each other, and winked at Oliver; while his next neighbours nudged him. Child as he was, he was desperate with hunger, and reckless with misery. He rose from the table; and advancing to the master, basin and spoon in hand, said: somewhat alarmed at his own temerity:

'Please, sir, I want some more.'

The master was a fat, healthy man; but he turned very pale. He gazed in stupified astonishment on the small rebel for some seconds, and then clung for support to the copper. The assistants were paralysed with wonder; the boys with fear.

Versus procedural MUD/IF English:

> Go south

You are in a dingy dining room. The room contains: Bill. Ted. Oliver Twist. The Master. Exits are: North.

> Examine master

The master is a fat, healthy man.

> Wait

Oliver Twist eats a gruel.

> Wait

Oliver Twist finishes eating a gruel.

> Wait

Oliver Twist approaches The Master.

> Wait

Oliver Twist says, “I’m still hungry.”

If a text MUD/IF could write as well as Dickens, I suspect it would be successful.

 

 


 

Posted on a game-designer’s private web-forum

March 13, 2012

 

you need a checkmate-detecting ai to ensure that players don't dead-end the game 1/3 of the way through. This ai looks forward in time to make sure that given the players prior bad choices, they can continue to have fun in the game. Very important! Games are limited to prebalanced simplelistic gameplay without this.

 

And a follow-up Post:

 

Another way to think about game design is this model: Grand-unified theory of world-like games.

 

 

1) You have AI's for every NPC. NPCs are contolled by their AIs MUCH-MORE than they are controlled by a movie-like script.

 

2) You have a predictive psychology model for the player... determining how intelligent (good at gameplay) they are, as well as what choices they are likely to make.

 

3) You have an "AI" predictor that looks forward through a (theoratical) ALL-possible-choices that the player can can make, as well as the NPCs. This will consume most of your computer's CPU.

 

If the AI-predictor determines that the player has gotten into a losing checkmate solution, say within the next 5 - 10 hours, then then it (a) stears the player into a non-losing direction, (b) stears the NPCs in a direction that won't encourage the player to lose (such as ensuring that a hard-to-find NPC suddenly needs to visit town more-often to pick up groceries), (c) makes gameplay easier, (d) etc.

 

 

 


 

 

Text-to-speech papers

 

I am also including my text-to-speech papers in this document, written for the “Blizzard Challenge”. I believe that text-to-speech is a CRITICAL technology for future virtual-world avatar-games. Contemporary computer-games limit themselves, by only using recorded speech-prompts for non-player-character dialogue. (http://www.synsig.org/index.php/Blizzard_Challenge)

 


 

 

Blizzard challenge 2007

Text-to-speech Designed For a Massively Multiplayer Online Role-Playing Game (MMORPG)

Mike Rozak

mXac, Darwin, NT, Australia

Mike@mXac.com.au, http://www.CircumReality.com

 

Abstract

CircumReality is a niche-market massively-multiplayer online role-playing game (MMORPG or MMOG) that relies heavily on text-to-speech (TTS) for narration, non-player character (NPC) speech, and text chat between players. Associated speech technologies enable voice chat and voice transformation/disguise. Most contemporary TTS engines are geared towards hand-held devices or telephony, resulting in technology that is not ideally suited for games. This paper discusses how a game-oriented TTS engine differs from a telephony or device-oriented TTS engine, and how those differences affect the technology.

1.      Introduction

In the early 1980’s, most computer games relied on “sprites” to generate their visuals. [1] Sprites are pixelated images that are moved around the screen by offsetting their X and Y origin. They are animated by quickly redrawing different images in the same location. Very few games used real-time 3D rendering in the 1980, the most memorable being Atari’s Battlezone.[2] Most real-time 3D rendering was used for CAD/CAM[3] and military flight simulators.[4] Twenty-seven years later, real-time 3D rendering in games is ubiquitous, and sprites are rarely used.

In 2007, most computer games use recorded speech, or even just text without any audio component. Recorded speech is expensive to produce, and requires enormous amounts of storage when used with a computer role-playing game (CRPG) or MMORPG. Everquest 2, a recent MMORPG, recorded over 200 actors [5] to produce 130 hours of voice-acting audio.[6]

No contemporary MMROPGs or CRPGs use TTS, just as in 1980, very few games used real-time 3D rendering. Current TTS isn’t acceptable for game developers because of the lack of emotion, and because contemporary TTS engines target telephony and handheld devices, resulting in some features that are net negatives for games. Recorded speech vs. TTS in 2007 is analogous to sprites vs. 3D rendering in 1980.

CircumReality is an experimental niche-market MMORPG [7] that is designed to survive financially with only a small base of players. Since recorded speech is so expensive and produces such large downloads, TTS is employed as a cost saving technology, as well as an enabler of new gameplay. Because of CircumReality’s niche-market nature, unemotional TTS, while still an issue, is not the show-stopper that it would be with mass-market games, where players demand the best visual and audio effects.

CircumReality also lets players create their own “world” that other players can enter. Based on statistics from older MMORPG development toolkits, [8] around 1% to 0.1% of the players are likely to create content. Many of these amateur authors will also wish to create their own TTS voices since that is part of the fun.

When CircumReality was first envisioned, a survey of TTS engines was conducted, but significant anti-game design decisions were found in existing engines:

1.     Many MMORPGs, such as the popular Runescape [9], are distributed to players for free. Such products earn money from advertising or virtual item sales.[10] Most TTS engines are licensed on a per-unit royalty, which isn’t acceptable for free-to-play games because they only earn income from around 10% of their players, and only from around 1% to 0.5% of all the copies of their software that are downloaded.[11]

 

2.     MMORPGs and CRPGs include up to a thousand computer-controlled non-player characters (NPCs).[12]  While having a unique voice for every NPC isn’t necessary, enough voices should exist that players don’t notice their re-use. Contemporary TTS engines focus on producing a few high-quality voices that, for the purposes of telephony, can easily require one hundred or more megabytes of RAM per voice.[13][14][15] MMORPGs require around a hundred voices, most procedurally derived from a few real voices, all fitting in a few hundred megabytes of RAM and download.

 

3.     In the case of CircumReality and many other games, some players will become authors and create their own content. This practice is often called “modding” [16] I expect that many authors will create their own text-to-speech voices, using their own voice or their friends’ voices for training. Most TTS engines don’t distribute their voice-creation tools, or make them easy enough for non-experts to use.

 

4.     Many of the authors will want to create non-English content. Precisely because CircumReality is targeting niche markets, many of the players who speak less-common languages will want to create a content that represents their language and culture. Such languages are not supported by contemporary TTS engines because the market it too small. Although not fully implemented, CircumReality’s TTS toolkit will allow motivated authors to create their own language with a minimum of linguistic knowledge.

This paper will examine how game-specific requirements affect TTS technology, and how the resulting design decisions impacted the Blizzard 2007 Challenge results.

The majority of the game-specific design decisions affect:

1.     The acoustic feature set – The voice must be stored in a compact format that is easily mutable into new voices.

2.     Speech recognition – ASR is not only used for segmentation, but for eliminating bad units and selecting the “best” units to keep.

3.     Unit concatenation – The acoustic feature set’s design has consequences for unit concatenation.

4.     Prosody model – The prosody model must be automatically generated with a minimum of linguistic knowledge.

 

2.      Acoustic feature set

MMORPG-oriented TTS needs to be able to synthesize enough different voices that players don’t notice duplications, while still retaining a download of a few hundred megabytes. Not only are approximately one hundred voices required, but some must be exotic-sounding voices, such as growling dragons and bell-like fairies.

 

To meet these flexibility requirements, the CircumReality TTS engine encodes voices using an acoustic feature set based on a two spectral envelopes: one for voiced and one for unvoiced, along with an F0, and per-harmonic phase. No residual is stored since residuals aren’t readily modified during voice transformation.

 

A screenshot of the feature set appears below. The top lines are the transcription, with F0 appearing underneath. The top histogram represents the voiced audio, and the bottom unvoiced. Each horizontal band in the histograms is one octave, with the lowest frequency being 100 Hz and highest 12.8 kHz. The bottom portion of the display is the colour-coded phase angle for 64 harmonics, with the fundamental at the bottom.

 

Figure 1: Acoustic feature set for “Fast, but endure”.

The original signal is encoded by detecting F0 as accurately as possible since even a small error in F0 detection causes severe errors in the encoded signal. To generate the spectral envelopes, a pitch period is stretched in time domain to a power-of-two width needed for an FFT. Adjacent pitch periods are also analysed, with differences in energy and phase in each harmonic being used to guestimate how much of the signal is voiced or noise; Noise has more variation in the harmonic’s energy and phase. Additional heuristics are used to minimize errors due to incorrect F0 estimation, such as using a windowed FFT for higher frequencies, as well as voiced vs. noise calculation. Phase is easily obtained from the FFT. All phases are rotated so that F0 always has a phase angle of 0.

 

The voiced signal is regenerated using additive sine-wave synthesis, adjusting for an interpolated phase. The unvoiced signal is synthesized using three sine waves per harmonic, with a randomly perturbed frequency whose variation is a function of the noise-to-voiced ratio. Using three randomized sine waves allows for smoother transitions from voiced to unvoiced than simply adding in a filtered noise signal, and provides more flexible monster-like voice transformations. The phase angles are also used for resynthesis; ignoring the phase information produces a “buzzy” voice.

 

This feature set has one major disadvantage: Because there is no residual, all of the voices have a slight “vocoder” sound. In the case of the voice used for the Blizzard 2007 Challenge, the vocoder sound was particularly noticeable.

 

Despite the feature set’s flaws, it has many game-specific advantages:

 

·       Voice variation:

o   Voice transformation/disguise algorithms can easily modify the spectral envelopes and produce new human-sounding voices.

o   Non-integer harmonics can be employed to generate unusual bell-sounding voices.

o   Looped waveforms can be used instead of additive sine-wave synthesis, good for growling monsters.

o   Converting portions of the voiced energy to unvoiced can also be used for monsters.

o   Some “emotions” such as whispering, speaking softly, or shouting also involve transformations of the acoustic feature set.

·       The feature set is easily compressed with a lossy encoding, producing smaller versions of a voice. Players with more memory and bandwidth can download larger uncompressed voices.

·       The feature set affects unit concatenation. See below.

·       Re-use of technology:

o   The same feature set, without F0 and phase, is used for ASR’s segmenter and unit scoring. (See below.)

o   The same feature set is used for encoding of voice chat. Voice chat includes the ability to transform/ disguise the players’ voices, using the same algorithms that modify the TTS voices.

o   The same feature set is used for automatic lip sync of player’s characters when they speak using voice chat.

 

 

3.      Speech recognition

Some players will create their own TTS voices as part of the effort to create content for their world. Either they or their friends will record several thousand utterances.

 

Consequently, the process of creating a voice must be reasonably automatic, and can’t require any speech expertise. For example: Authors can’t be required to review all the units and select the best one, nor can they be expected to speak exactly what they’re prompted to speak.

 

CircumReality’s voice-development toolkit employs ASR not only as a segmenter, but also as a filter to eliminate bad units, and to automatically select the best unit. ASR is included in the toolkit’s install and fits seamlessly into the toolkit’s user interface.

 

Traditional HMM-based speech recognizers split a unit into three-to-six time-slices, and create a frequency-domain mean and variance for each windowed time slice.[17, pp. 307-413] While this is adequate for dictation and phoneme segmentation, the speech recognition score can’t be used to accurately identify the “best” TTS unit for a given context. Most times, a simple mean/variance will choose a distinctly “poor” unit.

 

Poor selection happens because the “best” units often have the brightest, most prominent formants, narrow energetic peaks in the spectrum. The peak’s central frequency varies significantly throughout the frames of an individual unit, and more so across thousands of units. Averaging several thousand spectrums with narrowly-peaked formants together produces a single spectrum with blurry, ill-defined formants. A speech recognizer that uses the blurry formants to pick the “best” unit out of thousands inevitably chooses the unit that is most similar, which ends up sounding “muffled” and difficult to understand.

 

Many TTS systems avoid this problem by using speech recognition to eliminate the worst candidates, but leave the selection of the “best” up to luck and ASR-independent scores.[17, pp. 817] ASR-independent heuristics are used to identify the “best” candidate, such as unit duration, energy, and F0. ASR’s tendency to select muffled units is further minimized when synthesizers store contiguous unit sequences from the original data and select the longest contiguous candidate; a contiguous sequence of five or six phonemes only has a couple of other candidates as competitors, so even if mean/variance speech recognition were used to select the “best”, the phoneme sequence wouldn’t have enough available candidates to reliably select the most muffled candidate.

 

CircumReality’s TTS cannot leave unit selection up to chance, and it cannot rely on expert speakers or expert prompt reviewers. Amateur speakers are likely to misspeak or mispronounce the prompts. They are unlikely to review what they have recorded, failing to check for bad segmentation or pronunciations. Because of memory constraints for the voices, voice files won’t have long stretches of contiguous phonemes. Consequently, relying on ASR’s mean/variance to select a unit fails under the conditions that CircumReality will encounter.

 

CircumReality’s TTS voice generator attempts to work around muffled units by stochastically storing 48 exemplar spectrum-samples per sub-segment instead of storing one mean and variance.

 

Figure 2: 48 spectrum samples for each of 4 sub-segments, for “iy1” phoneme. Voiced is on top, with unvoiced below.

 

To produce a score for a feature-set spectrum time-slice, ASR performs a difference comparison between the spectral envelope in question and each of the 48 spectrums stored for the sub-segment of the recognition unit. Difference is calculated by performing a dot-product on the energy-normalized spectrums. A penalty is added based on the difference in total energies. The 48 scores are sorted, and the top 12% of the scores are combined together to produce the final score, using a weighted average.

 

By using only the top 12%, the speech recognizer is fairly confident that the score is representative of the unit. Considering the extremes, if scores from all 48 comparisons were averaged, the result would be similar to the single mean (and no variance) comparison common to ASR, preferring muffled formants. If only the top score were used, then incorrect units and alignments in the training set would easily derail the recognizer. Using the top 12% is a compromise.

 

Even using the stochasticly chosen spectrums, ASR has a tendency to choose muffled formants. To further minimize this propensity, the spectrum comparison algorithm is augmented to allow small frequency shifts in the formant peaks. Each octave of spectrum can be shifted plus or minus half an octave without penalty, as well as a +/- 3 dB energy change. This causes peaks with slightly different centre frequencies or energies to be treated as identical.

 

Even with these adjustments, ASR still tends to select the more muffled formants, particularly with the Blizzard Challenge 2007 voice. (See “Lessons learned”.)

 

CircumReality’s phoneme segmenter uses a Viterbi search, hypothesizing over all word pronunciations, and all possible phoneme durations. To create a score for a hypothesized phoneme, individual time-slice scores are averaged.

 

When building a TTS voice, a context-dependent (CD) ASR model is built for each phoneme, stochastically selecting the 48 x 4 exemplar spectrums from all of the matching CD phonemes in the model. If the data set is large, the context is the left and right phonemes, if small, the acoustic groups for the left and right phonemes. The ASR model of a CD phoneme is used to represent the “ideal” for the phoneme.

 

A “quality” score is generated for each unit to determine how accurately it matches the “ideal” for the CD phoneme. As per typical TTS unit-selection, differences between the duration, F0, and energy of the unit and those of the mean unit, are included as a penalty. The CD ASR score is also incorporated as a major portion of the unit’s quality score. The final quality score, which includes the CD ASR score, is stored with the unit to aid the concatenation Viterbi search.

 

Individual units, diphones, and contiguous sequences of units are selected using a weighted average of the individual unit scores. For a given sequence, the candidate with the highest aggregate quality score is retained.

 

 

4.      Unit concatenation

CircumReality unit concatenation is fairly traditional. [17, pp. 804-817] A Viterbi search tries to find the highest scoring sequence of units. Demiphones are used.

 

Target costs include:

·       The unit’s quality score, a major portion of which is the CD ASR score, is used.

·       Differences between the original and target unit’s F0, F0 delta, energy, and duration have minimal effects.

·       Penalties for CD phoneme mismatches are also included. Small penalties are used if the left/right phonemes differ from the desired context only in phoneme stress. Larger penalties are used is the left/right phonemes differ, but are from the same acoustic group.

 

Join costs include:

·       The difference between the two edge-spectrums, calculated using the same difference measure employed by ASR.

·       Weighting to encourage contiguous units is important. Penalties for non-contiguous units are amplified if either of the units is plosive since the acoustic feature set does a better job of joining non-plosive unit halves. Breaks are preferred between two non-plosive units, or in the middle of a single non-plosive unit.

·       F0 and the harmonic’s phase are not used.

 

Concatenated units are smoothed by pitch-shifting and energy-adjusting the formants at the boundaries. These adjustments, while easy to perceive in the visualized feature set, provide only a minor improvements to the voice.

 

Phase is blended at non-contiguous unit boundaries. Around 1/16th of a second is blended for the lowest harmonics. Higher harmonics do not require blending. Failure to blend phase is most noticeable in the low-F0 Blizzard voice.

 

 

 

 

 

 

5.      Prosody model

A prosody model is learned from the voice. This is critical for two reasons: (1) It helps make each voice sound more unique, and (2) Learned prosody simplifies localization, important for authors customizing CircumReality to their own language; they won’t have professional linguists to hand-generate prosody rules.

 

To train prosody: F0 and energy curves are calculated for the utterances. Phoneme segmentation, word segmentation, part-of-speech, and punctuation are also used. A list of syllables is extracted from the audio sample, with punctuation being treated as a silent syllable. The following information is extracted for each syllable:

·       The average F0 of the syllable, the rise of the F0 over the syllable, and the amount of “bend” in the F0. Three values produce a second-order approximation of the F0 curve. These numbers are relative to the sentence’s average F0.

·       The average energy of the syllable, relative to the sentence’s average energy.

·       The duration of the syllable relative to the duration predicted by the syllable’s phonemes, and the duration of the syllable relative to the average duration of all syllables.

·       The number of phonemes in the syllable.

·       What word the syllable came from, particularly important for function words.

·       Whether the syllable is stressed or unstressed.

·       The syllable index number within the word.

·       Whether there is silence before the beginning of the syllables that begin words.

·       The depth of the word in the context-free grammar (CFG) parse tree used for part-of-speech disambiguation.

 

The syllable information is used to create a number of simple prosody models, such as:

·       How stressed/unstressed syllables affect F0, duration, and energy.

·       How the location within the sentence of length N affects F0, duration, and energy.

·       How the previous and subsequent parts-of-speech and stresses affect F0, duration, and energy.

·       The silence bit is used to determine the probability of a short pause before the word given the part-of-speech context, often indicative of a phrase break.

 

Just as using a single mean and variance for ASR produces muffled units, using simple models for prosody produces uninteresting and bored-sounding prosody. To work around this problem, the prosody generated by the simple prosody models is “subtracted” from the original sentences, which are then stored in the prosody model as “residuals”.

 

When prosody is synthesized, the synthesizer finds the longest contiguous segment of residual that’s the best match. A “contiguous segment” requires an exact match for the syllables’ part-of-speech and stressed/unstressed values. The quality of the match is determined by differences in the numbers of phonemes in each syllable, the syllable index within the word, and the original word (important for function words).

 

If only the longest/best match is chosen, however, the synthesized prosody becomes a somewhat erratic, speaking some words far too quickly, slowly, loudly, or quietly.

 

To stabilize the system, the top residual choice is compared to the next seven-best residual choices. A difference score is calculated by comparing the difference in the synthesized prosodies for each of the candidates, using F0, duration, and energy. The four most-similar prosodies are averaged together, largely eliminating the erratic prosody, while still maintaining interesting prosody.

 

To regenerate the prosody, the simple prosody models are first run, and then the prosody residual is incorporated. Adjustments are made where the synthesized sentence doesn’t exactly match the sentence used to generate the residual. For example: If the synthesized prosody has a syllable with three phonemes, but the chosen residual had only two phonemes for that syllable, then the syllable will be lengthened according a database value that was generated as part of the prosody model.

 

This prosody model has several advantages:

·       Every voice has its own prosody, making the voices sound more unique.

·       It requires no linguists (or any input) for localization. Unfortunately, part-of-speech determination still requires a context-free grammar with a few dozen hand-programmed linguistic rules.

·       Prosody can be transferred or merged between voices.

·       Components of the prosody model can be procedurally adjusted to create new prosody models from the original.

·       If an author has a problem with a common phrase not being spoken properly, he merely has to record how it should be spoken, and add that recording to the TTS or prosody-model training set.

·       The residual match scores are randomly perturbed so that a sentence is spoken differently every time it is heard, which is particularly important in a game where players may repeatedly hear the same sentence, such as “You open the door.”

 

 

6.      2007 Blizzard challenge

The CircumReality TTS engine didn’t perform well in the 2007 Blizzard Challenge, coming in last for the mean opinion score and similarity tests.

 

 

Figure 3: Mean opinion score for all listeners.

 

 

The major problems with the voice were:

 

·       Existing voice encoding/decoding algorithms didn’t work well with the Blizzard Challenge voice, producing a noticeable “vocoder” effect. While the vocoder effect occurred with other test voices, it was much more noticeable with the Blizzard 2007 voice.

 

Switching to a different method for encoding the voice, or adding a residual, would eliminate the vocoder effect, but it would also hinder the engine’s ability to procedurally create a variety of voices from a single source.

 

·       Speech recognition tended to select muffled units for the voice. I think this happened because the brightness of the formants varies greatly between units, a useful skill for a professional voice talent trying to create an expressive sentence. However, ASR ends up learning that the “best” brightness for a unit is someplace between bright (and easy to understand) and muffled, which results in slightly muffled units, suboptimal for a TTS voice.

 

Oddly, the engine did relatively better with the word error rate:

Figure 4: Word error rate for all listeners.

 

One explanation for this discrepancy is that despite the “vocoder” distortion caused by encoding to the acoustic feature set, and unit selection preferring “muffled” units, ASR successfully chose one of the “better” muffled units, although not the “best” un-muffled unit.

 

Synthesized prosody performed better than I expected. Prosody quality wasn’t isolated out by any of the Blizzard tests, but my own non-scientific comparison of the CircumReality prosody to that of other engines puts it closer mid-range. It sounded less stiff and more natural than many of the engines, probably because it doesn’t use any hand-generated prosody rules. Ironically, natural-sounding prosody was also a minus: The Blizzard speaker’s volume often trailed off near the end of a sentence, and so too did CircumReality’s synthesized prosody. CircumReality’s synthesized prosody performs less well on long sentences though.

 

 

7.      Lessons learned

Since the listening test samples for the Blizzard challenge were submitted, CircumReality’s algorithms have been improved so that the Blizzard 2007 voice sounds noticeably better. The improvements that made the greatest difference were:

 

·       Due to memory constraints, the voice generator tool couldn’t produce a voice larger than 20,000 units on 32-bit Windows. This wasn’t a problem for a game-oriented voice since 20,000 units, around 100 megabytes uncompressed, is at the extreme upper range of acceptable voice size. Multiplied by 8 voices, this produces an 800 megabyte download, which is far too large for most players. After installing 64-bit Windows Vista and recompiling the toolkit for 64 bits, a better-sounding larger voice was produced.

 

·       The algorithms to extract the acoustic features from the Blizzard 2007 voice needed improving. The voice’s combination of low F0, well defined, crisp formants, large dynamic range, large F0 range, and occasional vocal fry all proved troublesome. The improved feature extraction algorithms noticeably reduced the “vocoder” sound of the voice, although it still exists. Even though storing a residual would eliminate this, doing so would impair the flexibility of voice transformation, an important feature for games.

 

·       Unit scoring has been improved so that when the “ideal” units are trained using ASR, training is weighted by the unit energy, causing ASR to prefer the louder units, which also tend to have brighter and more well-defined formants.

 

·       Unit selection has been augmented to store longer unit sequences, as well as store multiple versions of a unit sequence based on F0. Again, such affordances produce better-sounding but larger voices, less suited for games.

 

Frequency shifting a unit of the Blizzard 2007 voice had significantly worse effects on quality than the other voices that CircumReality was tested with. I think this is because of errors in the harmonics’ phases: Looking at the visual representation of the feature set, it’s obvious that phase tracks with the formants. Harmonics at the peak of the formant incur the least amount of phase delay, while those at the edges more. Importantly, harmonics outside a formant are so quiet that their phase is undetectable or completely inaccurate. When a unit’s F0 is shifted, the phase relationship with the formant is broken; the harmonic at the peak of the formant no longer has the lowest phase delay. Even worse, some of the undetectable harmonics are frequency-shifted into the formant, and consequently resynthesised with either a random or 0-degree phase. The Blizzard 2007 voice illuminated the problem because it had a low F0 and narrower formants.

 

 

8.      Future work

Although acoustic feature extraction still induces noticeable errors, the largest problem with the CircumReality TTS engine, in terms of games, is the synthesized prosody. Even if prosody were as good as the best prosody example in the Blizzard Challenge, it wouldn’t be good enough; even the best TTS engines produced speech that sounds like a “bored telephone operator”, destroying any illusion that the NPCs are real.

 

Recorded speech is much more emotionally powerful. To experience an example of NPC interaction with emotionally recorded speech, see the Facade [18] interactive storytelling [19] demo. Facade’s 167 megabyte distribution also illustrates the problems with recorded speech: Most of the download size is used for speech audio recordings of only two NPCs speaking in an extremely limited domain. Extrapolating to a thousand NPCs with a much broader conversation domain reveals the ultimate impossibility of using recorded speech, despite its emotional quality.

 

For the next 10-20 years, I suspect the best solution to this problem will be to use transplanted prosody wherever possible. Of course, many phrases that NPCs speak are procedurally generated and must still revert to synthesized prosody.

 

The key to good transplanted prosody is the integration of the transplanted prosody tools into the game development toolkit. Recording an utterance for transplanted prosody needs to be so convenient, quick, and easy that authors automatically and effortlessly record the transplanted prosody utterance when they type in a sentence for a NPC to speak.

 

Thus, the key to better game TTS isn’t just the algorithms, but also TTS’s integration into the game-development toolkit.

 

The tools for producing a CircumReality TTS voice are publicly available as part of the “3D Outside the Box” software package on http://www.mXac.com.au/m3d. The CircumReality game, still not finished, is available from http://www.CircumReality.com. It uses many of the open-license voices from the 2005 Blizzard Challenge.

 

 

 

9.      References

[1]     http://en.wikipedia.org/wiki/Sprite_%28computer_ graphics%29

[2]     http://en.wikipedia.org/wiki/Battlezone

[3]     http://en.wikipedia.org/wiki/CAD/CAM

[4]     http://en.wikipedia.org/wiki/Flight_simulator

[5]     http://pc.gamezone.com/news/08_02_04_10_57AM.htm

[6]     http://www.mobygames.com/game/everquest-ii

[7]     http://en.wikipedia.org/wiki/Mmorpg

[8]     http://www.mudconnect.com/ - Around 1650 text MUDs have been created by a community of 50,000 to 200,000 players.

[9]     http://www.runescape.com/

[10]   http://en.wikipedia.org/wiki/Real-money_trading

[11]   http://en.wikipedia.org/wiki/Shareware

[12]   http://en.wikipedia.org/wiki/Non-player_character

[13]   http://www.cepstral.com/downloads/

[14]   http://www.naturalvoices.att.com/products/tts_data.html

[15]   http://www-306.ibm.com/software/pervasive/

voice_server/technical_details/

[16]   http://en.wikipedia.org/wiki/Mod_%28

computer_gaming %29

[17]   Huang, Xuedong, Acero, Alex, and Hon, Hsiao-Wuen, Spoken Language Processing, A Guide to Theory, Algorithm, and System Development, 2001, New Jersey, Prentice Hall PTR.

[18]   http://www.interactivestory.net/

[19]         http://en.wikipedia.org/wiki/Interactive_storytelling

 


 

 

Blizzard Challenge 2008

CircumReality functionality delta: Blizzard Challenge 2007 to 2008

Mike Rozak

mXac, Darwin, NT, Australia

Mike@mXac.com.au, http://www.CircumReality.com

 

Abstract

Although performing poorly in the Blizzard Challenge 2008, the CircumReality text-to-speech engine improved significantly from the Blizzard 2007 test. The engine’s acoustic model, prosody model, and acoustic synthesis were improved between tests. This paper discusses the CircumReality engine’s test results and reasons why it did poorly. The paper provides a list of improvements that resulted in higher test scores in 2008, as well as implementation details one change: objectively-calculated target costs.

 

Index Terms: speech synthesis, games, Blizzard Challenge

Introduction

The Blizzard Challenge was devised “to better understand and compare research techniques in building corpus-based speech synthesizers on the same data. The basic challenge is to take the released speech database, build a synthetic voice from the data and synthesize a prescribed set of text sentences. The sentences from each synthesizer will then be evaluated through listening tests.[1] Participants then write a paper discussing their results. Over the course of years, the intent of the competition and publication cycle is to improve the quality of TTS engines.

The CircumReality TTS engine is designed for the CircumReality multiplayer online game. [2] The engine uses concatenative synthesis with a trained prosody model. Half-phone units are used with a triphone context.

The engine was first entered in the Blizzard 2007 challenge and did poorly, ranking last on nearly all the tests.[3] Although the CircumReality engine ranked poorly in the latest Blizzard 2008 challenge, its scores improved significantly from 2007. This paper discusses why the CircumReality engine did poorly, what changes to the engine significantly improved the quality, and implementation details of one change: objectively calculated target and join costs.

Blizzard challenge 2008 results compared to 2007

Although CircumReality TTS engine did poorly in the 2007 and 2008 challenges, it showed significant improvement.

CircumReality’s mean-opinion score (MOS) rose 0.7, from 1.3 for the “A” voice in 2007, to 2.0 in 2008. (See Figures 1a and 1b.) The average of all other engines’ MOS (excluding the original speaker and two 2008 benchmark engines, Fest and HTS) was 2.95 in 2007 and 2.92 in 2008, down slightly. Of course, the 2007 and 2008 voices were different, so only large changes in score or relative to other engines are meaningful. Nor are the participants in 2008 the same as 2007.

In contradiction, the Festival benchmark engine’s MOS improved from 3.0 to 3.3 despite the average engines’ MOS declining slightly. Since participants in both years are largely the same, I suspect this represents either an engine bias towards American English, or voices recorded with 2007’s “news presenter” prosody.

 

Figure 1a: Blizzard 2007 MOS

 

Figure 1b: Blizzard 2008 MOS

CircumReality performed relatively better with the word-error-rate test (WER), both in 2007 and 2008:

 

Figure 2a: Blizzard 2007 WER

Figure 2b: Blizzard 2008 WER

CircumReality’s mean WER only dropped 2%, from 47% in Blizzard 2007 to 45% in Blizzard 2008. The mean WER for all other voices (excluding the original speaker and two 2008 benchmark voices, Fest and HTS) was 35% in 2007, and 40% in 2008, an increase of 5% due to the 2008 voice’s ebullient British prosody. The benchmark engine, Festival, also had an increased WER, increasing from 25% in 2007 to 35% in 2008. Despite the aggregate WER increasing, CircumReality’s WER decreased marginally.

Failure analysis

Acoustic model

As discussed in CircumReality’s Blizzard 2007 paper [3], the CircumReality engine uses an acoustic feature set consisting of a voiced and unvoiced spectrum. The feature set was chosen to enable easy voice transformations, important for games. Unfortunately, the feature set introduces vocoder-like artifacts, more prominent in some voices than others. The 2007 CircumReality TTS engine (CR2007) used additive sine-wave synthesis to synthesize the wave.

The Blizzard 2007 voice exposed many problems with the acoustic feature extraction. Between Blizzard Challenges, the feature extraction algorithms were improved, but the Blizzard 2008 voice still exhibited significant artifacts.

The feature set had pitch-synchronous PCM added. A full wavelength was included with each frame, and time-stretched to the required wavelength when synthesized. PCM functionality was originally included in the feature set for testing and debugging purposes only. Its functionally was kept to a minimum since PCM isn’t flexible enough for voice transformation, an important feature for game synthesis. Consequently, TD-PSOLA was not implemented to save development time, despite TD-PSOLA sounding better.

The 2008 CircumReality TTS engine (CR2008) could synthesize with either PCM or additive sine-wave synthesis. PCM synthesis improved acoustic naturalness, but introduced other errors:

Overlapping pitch-synchronous PCM with a Hanning window reduces unvoiced energy at high frequencies, particularly impacting the “brightness” of the “s” phoneme. Energy at high frequencies was amplified to counteract this effect, improving the intelligibility of “s”. Unfortunately, the high-frequency energy boost changed the voice’s quality, impacting the “similarity” score.

PCM is a proverbial double-edged sword. PCM produces high fidelity speech synthesis, but introduces large artifacts when pitch shifted. The target costs for pitch-shifting PCM were calculated and included in the unit-selection search. (See section 5.). The costs, as expected, were large.

Even using large F0 target costs, the unit-selection search would occasionally select a unit requiring a substantial pitch shift, producing “hiccups” in the speech synthesis. The hiccups undoubtedly lowered the voice’s MOS, counteracting some of the MOS improvements gained by using PCM.

To minimize the hiccups, units’ original F0 contours were averaged into the synthesized-prosody’s F0 contours, producing higher acoustic quality at the expense of lower prosody quality.

Extremely high F0 target costs outweighed all other join and target costs: duration, energy, and phoneme context. Consequently, the use of PCM forced less well-fitting units to be substituted, reducing the voice’s quality.

At the time of the 2008 test, PCM sounded marginally better than additive sine-wave synthesis, despite all its negative consequences. PCM was used for the tests.

Prosody model

For computer games, personality is often more important than intelligibility. CircumReality’s synthesized prosody is designed to try and reproduced the prosody of the training voice, often at the expense of intelligibility. CR2008’s prosody algorithms produce lower-quality prosody than hand-generated rules.

The 2008 speaker spoke in an “ebullient” manner that, even before synthesis, was more difficult to understand than the “news presenter” prosody spoken by the 2007 Blizzard Challenge voice.

Ebullient speech exposed weaknesses in CR2008’s prosody algorithms that weren’t as obvious in the “news presenter” voice from 2007. CR2008 wasn’t able to synthesize ebullient prosody that well, certainly less well than it could synthesize “news presenter” prosody.

CR2008 did manage to approximate ebullient prosody. Unfortunately, in partially succeeding, CR2008’s prosody modeling made the voice more difficult to understand because ebullient prosody is inherently more difficult to understand than “news presenter” prosody, even when spoken by a real person.

 

Figure 3: Blizzard 2008 similarity scores

When listening to the test sentences, I thought CircumReality mimicked the voice’s prosody better than many of the other engines. I expected CircumReality’s similarity score (see Figure 3) to be relatively higher than its MOS score. This didn’t happen; both the MOS and similarity scores, and their positions relative to other engines, were approximately the same. Either my perception of how well CircumReality mimics prosody is incorrect, or prosody is only a very minor part of how people perceive voice similarity. Acoustic similarity seems to be a much larger component, at least when listeners are presented with an unfamiliar voice. If the voice were Winston Churchill’s, with its own unique prosody, would prosody modeling count for more?

CR2008’s prosody model failed in other ways:

As already stated, the prosody model was hindered by the need for PCM to minimize F0 changes.

Synthesized prosody was further impaired by problems with F0 detection. Pitch doubling would occasionally happen, particularly at the end of utterances. Units that are pitch-doubled are normally eliminated from the acoustic model because an F0-mismatch results in distorted features that produce a low ASR score; low-scored units are automatically discarded. The prosody model doesn’t have any equivalent F0 integrity checks, so a pitch-doubled word results in synthesized prosody that suddenly doubles F0 for a word or two, usually at the end of a sentence.

Changes between Blizzard 2007 and Blizzard 2008

Below is a list of major changes between CR2007 and CR2008. All of these changes produced at least minor improvements to voice quality. Some will be discussed in detail, in section 5.

General changes

·       Bugs that produced minor reductions to speech quality were found and fixed.

Acoustic model

·       F0 detection was improved by assuming that F0 stayed near to the median F0 throughout the sentence.

·       Acoustic feature extraction was improved. The same features were used, but new algorithms more-accurately extracted the features from the training utterances.

·       Pitch-synchronous PCM was stored, allowing CR2008 to synthesize using PCM.

Unit selection

·       CR2007’s voice-construction tool ran out of 32-bit memory when building the Blizzard 2007 voice, limiting the voice to 20,000 units. CR2008’s tool was rebuilt with 64-bit pointers, easily allowing a 265,000 unit voice.

·       When building a voice, all units were scored by a combination of ASR and target costs based on F0, duration, and energy. CR2008 automatically discarded the bottom 25% of all units to minimize bad units.

·       In CR2007’s voice building tool, an ASR model for each triphone was trained and used to compute the unit’s score. In CR2008’s tool, nine ASR models for each triphone model were calculated as a two-dimensional matrix of low, medium, and high F0 by low, medium, and high energy. This improved the voice’s clarity and eliminated some “muffled” units.

·       In CR2007’s voice building tool, ASR was used to test how similar the unit sounded compared to its triphone model. In CR2008, this value was modified based on how differently the unit compared to other phonemes, comparing the unit against ASR models for similar phonemes. This discouraged units that were in-between two phonemes, producing a voice that was easier to understand.

·       CR2008 differentiates between triphones at the start, middle, or end of a word. CR2007 did not.

Prosody model

·       The prosody model was refined. The same basic principles were used.

Prosody synthesis

·       In CR2008, F0 and duration of synthesized units are now affected by the original unit’s F0 and duration. When PCM acoustic synthesis is enabled, F0 from the original unit is weighted much more strongly than the F0 from the synthesized prosody model.

Acoustic synthesis

·       The acoustic-unit-selection Viterbi-search was refined.

·       The acoustic unit search for CR2007 voice used ad-hoc target and join costs. For CR2008, these were calculated using ASR. See section 5.

·       F0, duration, and energy of the prior unit are included in the target cost to encourage smooth transitions when non-contiguous units are used.

·       As stated earlier, CR2008 can synthesize using PCM instead of additive sine-wave synthesis.

Objectively calculating target and join costs

Once change between CR2007 and CR2008 warrants further discussion.

In CR2007, target costs were based on ad-hoc guestimates. For example: Left/right context substitutions were assigned a very high target penalty, around ten times higher than F0, energy, and duration target costs.

The MARY-TTS Blizzard 2007 paper [4] implied that objective target costs for concatenative synthesis hadn’t been calculated before. This challenge intrigued me so I decided to calculate the costs. I later learned that the USTC/iFlytek Blizzard 2007 paper [5] discussed target cost calculations for HMM synthesis. I’ll discuss the differences later.

The target-cost calculating tool was created, and values were calculated from 9000 sentence-length recordings of my own voice. What follows is a list of the calculated target-cost values, and the algorithms used to calculate them.

F0 target cost

To calculate F0 target costs, an ASR model was trained for every triphone. (To reduce computation time and memory, the left and right content phonemes of the triphone were grouped into one of 17 groups. For example: “m”, “n”, and “ng” were placed in the same group.) Importantly, not all versions of the triphone unit were included in the ASR model; only phonemes whose F0 fell near the median F0 for the triphone were trained.

In a second pass, all of the phonemes in the training data were compared against the F0-limited triphone ASR models. The ASR scores were graphed on a scatter plot.

 

Figure 4: F0 target costs for (l, r) – eh1 – (m, n, ng). The vertical axis shows the ASR score, with large values being poor matches. The horizontal axis shows the number of octaves that F0 was above or below the triphone’s median F0.

To ensure that enough data existed to produce an accurate linear fit, the data points were combined into four sets based on broad phoneme categorization. Phonemes were categorized into voiced (V) or unvoiced (U), and plosive (P) or non-plosive (N). For example: The phoneme, “m”, is voiced non-plosive (VN), while “t” is unvoiced plosive (UP).

 

 

Per octave higher

Per octave lower

UN

3.26

6.97

UP

2.53

1.13

VN

5.43

6.52

VP

3.76

3.71

Table 1: F0 target costs per octave the target is higher or lower than the original data.

The calculated F0 target costs, although lower than expected, make intuitive sense; F0 target costs for unvoiced plosives (UP) are much lower than costs for voiced non-plosives (VN).

F0 target costs with PCM

The F0 target costs in 5.1 were calculated assuming that additive sine-wave synthesis would be used. Synthesizing with PCM requires additional target costs since even small F0 shifts in PCM produce extreme artifacts.

To calculate the PCM F0 target costs, the voiced and unvoiced spectrums were shifted up and down by half an octave, simulating a PCM F0 shift of half an octave. The shifted spectrums were compared against the triphone ASR models. The ASR score for the original un-shifted unit was also calculated, and subtracted from the two shifted ASR scores. All the shifted scores were averaged based on UN, UP, VN, and VP.

 

 

Per octave higher

Per octave lower

UN

18.28

18.82

UP

11.06

7.44

VN

20.94

19.8

VP

10.9

8.8

Table 2: PCM F0 target costs per octave the target is higher or lower than the original data.

PCM F0 target-cost values are very high, especially for non-plosives (UN and VN). Because TD-PSOLA has fewer acoustic artifacts than the simplistic PCM synthesis I used, I suspect that TD-PSOLA would have produced lower F0 target-costs, although still significant.

Energy target costs

Energy target cost was calculated using the same basic approach as F0 target costs. Instead of training an ASR model with F0’s near a target, only units with an energy-value near the target energy were trained.

 

 

Energy doubled

Energy halved

UN

5.95

4.94

UP

6.58

1.62

VN

4.04

6.37

VP

7.11

3.13

Table 3: Energy target costs based on the target’s energy relative to the unit’s original energy.

Duration target costs

Duration target costs were calculated in the same way that F0 and energy target costs were calculated.

 

 

Duration doubled

Duration halved

UN

0.05

1.5

UP

3.02

15.53

VN

1.04

5.04

VP

4.45

4.81

Table 4: Duration target costs based on the target’s duration relative to the unit’s original duration.

Start/end word target costs

CR2008 differentiates units occurring at the start, middle, or end of a word, and applies a target-cost penalty if there’s a mismatch.

To calculate the target cost, four ASR models were trained per triphone: At the start of a word, middle of a word, and end of a word, and triphones that were the entire word.

A second pass compared every phoneme in the training data against each of the four ASR models for the triphone. The ASR score from the correctly-matched word-position model was subtracted from all the others so the target-cost penalty for an exact match is always 0.

All of the ASR scores were averaged:

 

 

Target-cost penalty

No mismatch

0.00

Start-of-word mismatch

3.56

End-of-word mismatch

3.38

Start and end-of-word mismatch

5.61

Table 5: Target-cost penalty for word-position mismatches.

Mismatched left/right context target costs

CircumReality’s unit selection search can substitute in a different triphone with the same centre phoneme. For example: The “a” in “cat” might be used to synthesize the words “cap” or “bat” even though the “a” in “cap” and “bat” should use different triphones.

Calculating the context target costs requires an enormous number of ASR models to be trained:

 

·       Exact match ASR models – An ASR model was trained for every triphone, using the left and right phonemes as the left and right triphone context.

·       Narrow-group ASR models – The left and right phonemes were categorized onto one of 17 groups, and the triphone trained based on the left and right phonemes’ groups.

·       Broad-group ASR models – The left and right phonemes were categorized into one of 5 groups, and the triphone was trained based on the left and right phonemes’ groups.

 

In a second pass, every phoneme in the training database was compared against numerous “exact match”, “narrow group” and “broad group” ASR models:

 

1.     “Exact-match” target cost– The phoneme was compared against the appropriate “exact match” ASR model.

2.     “Mismatched-stress” target cost – If the right context was a stressed or unstressed phoneme, then the phoneme was compared against the “exact match” ASR model of the opposite stress-context. For example: If the right context was “eh1”, the phoneme was compared against the model with “eh0” as the right context.

3.     “Mismatched-phoneme in narrow group” target cost – The phoneme was compared against all the “exact match” ASR triphone models with varied right contexts, such that: (a) the right context phoneme was part of the true right-phoneme’s “narrow group”, and (b) the right context’s phoneme was not a stressed or unstressed version of the true right-context phoneme. The ASR scores were then averaged.

4.     “Mismatched-phoneme in broad group” target cost – The phoneme was compared against all possible right-context variations of the “narrow group” triphone ASR models, except the phoneme’s true “narrow group” ASR model. The results were averaged.

5.     “Mismatched-phoneme not in broad group” target cost – The phoneme was compared against all the “broad group” triphone ASR models whose right-context did not match the phoneme’s true right context. The results were averaged.

6.     To ensure that an exact left/right context match would have 0 target cost, the “exact match” score from step 1 was subtracted from all the other scores (steps 2 through 5).

7.     Steps 1 through 6 were repeated, but the left context was varied instead of the right.

 

The resulting target costs are:

 

 

Left context mismatched stress

Left context mismatched phoneme in narrow group

Left context mismatched phoneme in broad group

Left context mismatched phoneme not in broad group

UN

0.67

1.30

1.67

2.90

UP

0.3

1.47

2.13

4.84

VN

0.43

0.99

1.51

2.22

VP

0.51

1.20

1.60

5.76

Table 6a: Left context target costs.

 

Right context mismatched stress

Right context mismatched phoneme in narrow group

Right context mismatched phoneme in broad group

Right context mismatched phoneme not in broad group

UN

1.28

7.41

7.44

5.87

UP

2.16

4.83

5.29

11.21

VN

2.71

3.30

2.62

4.36

VP

3.02

7.46

5.61

8.54

Table 6b: Right context target costs.

As anticipated, mismatched plosives (P) tend to incur a higher target cost then non-plosives (N). Unexpectedly, right-context-substitution target costs are much higher than left-context costs.

Context target costs were much lower than anticipated, less than one tenth the ad-hoc values used in CR2007.

The data also illustrates some noise. Theoretically, all values should be monotonically increasing from left to right. Values don’t always do this, as in the case of the right context being an unvoiced non-plosive.

Non-contiguous join costs estimates

Join costs between two units were calculated using a distance measure between the spectrums of the two boundary frames. With thousands of possible joins considered by the unit-selection Viterbi search, this process can be very slow. Pre-calculating all the join costs is not an option for CR2008 due to the memory and file size requirements of games.

As an optimization, the “mean join costs” based on diphones are calculated and used to reduce the number of candidates for which accurate boundary scores must be calculated. The unit-selection search’s hypothesized units are narrowed down to the top 100 candidates by sorting based on their anticipated scores, including all the target costs and the mean join costs. Join-costs are then accurately calculated between the existing hypothesis and 100 new candidates. (See section 5.8.)

To calculate the mean join costs, a diphone ASR database is trained. Unlike the other ASR training, only the last frame in the first unit is trained – where the join occurs. CR2008’s ASR comparison for a single frame is exactly the same mathematics as used to calculate join costs.

ASR scores for the diphones are calculated and averaged into a database. As with other target cost calculations, phonemes are categorized into one of four groups:

 

 

Right context is UN

Right context is UP

Right context is VN

Right context is VP

Left context is UN

20.98

17.38

17.88

13.75

Left context is UP

23.42

25.67

16.51

22.20

Left context is NV

21.42

22.52

11.80

15.89

Left context is VP

24.71

31.30

12.59

13.46

Table 7: Mean join-costs given the left and right contexts.

Join costs

Join costs between non-contiguous units were calculated using a distance measure between their adjacent spectrums. As shown earlier (Section 5.7), the mean join cost was around 20, with higher values indicating poorer joins.

The unit’s score and all the target costs have a “per second” connotation. When the unit-selection Viterbi search includes them in the hypothesis score, the ASR scores are scaled by the unit’s duration.

Join costs are different because they are the calculation of an instantaneous value, over one frame, not the duration of the phoneme. Because join cost is instantaneous, the join cost value should theoretically be scaled by one frame (5 milliseconds) and added to the Viterbi search score.

This doesn’t work well in practice. A scaling value of 25-50 milliseconds produces better-sounding results. I haven’t yet determined why the theoretical value doesn’t work well.

USTC/iFlytek

I hadn’t noticed the Blizzard 2007 USTC/iFlytek paper [5] discussing their target and join cost calculation methods until long after implementing my own algorithms.

CR2008’s target/join-cost algorithms differ from those iFlytek’s in a number of ways:

·       CR2008 uses a different acoustic feature set than iFlytek, so per-frame acoustic distance calculations are handled differently, but with the same intent.

·       iFlytek uses F0 as a join cost, probably because the iFlytek’s acoustic synthesis employs a PCM acoustic representation. Since PCM doesn’t handle pitch bending well, little to no pitch bending would be applied to iFlytek’s synthesized units, leaving only F0 mismatches at joins. CR2008 is designed for games, where transplanted prosody is required. F0 is dictated by the transplanted prosody or prosody model. Synthesized F0 is never the same as the original unit’s F0. Consequently, F0 must be part of the “per second” target cost instead of the instantaneous join cost.

·       iFlytek’s duration model is built into the same framework as its acoustic and concatenation models. F0 and energy are also included in that framework. CR2008 separates F0, duration, and energy into a separate model. They’re either provided by a prosody model or transplanted prosody. In CR2008, the prosody model drives F0, duration, and energy, in turn driving the unit selection. Conversely, iFlytek’s HMM synthesis, with no explicit prosody model, appears to let unit selection drive F0, duration, and energy, which in turn drives prosody.

·       Context mismatch costs are implicitly handled by iFlytek’s HMM acoustic distance measures. CR2008 must explicitly include them.

 

Conclusions and future work

The CircumReality TTS engine improved significantly between the 2007 and 2008 Blizzard Challenges. This was achieved through a variety of changes in the acoustic model, unit selection, prosody model, and acoustic synthesis.

Using PCM in CR2008 was a mistake; although it improved the acoustic quality of the voice, PCM’s F0 inflexibility hurt many other TTS subsystems. TD-PSOLA is expected to sound better, but isn’t ideal for games, so it may not be worth the experimental effort. CR2008’s acoustic feature extraction algorithms have been improved in the months since submitting synthesis results for the 2008 Blizzard Challenge. The quality of the additive sine-wave synthesis voice now matches or exceeds the PCM voice.

I plan to improve acoustic synthesis in a number of ways:

1.     More-accurate ASR, since ASR is the foundation of good unit selection.

2.     More-accurate unit scores, such as different scores for each half of the unit.

3.     More-accurate target and join costs, using more than the four groups (UN, UP, VN, VP) discussed here.

4.     The join-cost scaling problem from 5.8 needs to be solved.

5.     Smoother unit joins are needed, the exact location of the join determined by ASR.

6.     Prosody tradeoffs that improve the aggregate (unit + target + join) scores for a synthesized utterance.

 

The prosody model needs to be improved too, although the tradeoffs between intelligibility and mimicking the original voice’s prosody will continue to be an issue.

References

[1]   Anonymous, “The Blizzard Challenge 2008”, CMU. Online: http://festvox.org/blizzard/, accessed on 4 July 2008.

[2]   Rozak, M., “What is CircumReality?”, mXac. Online: http://www.CircumReality.com, accessed on 16 July 2008.

[3]   Rozak, M., “Text-to-speech Designed for a Massively Multiplayer Online Role-Playing Game (MMORPG)”, in The Blizzard Challenge 2007, Bonn, Germany. mXac. Online: http://festvox.org/blizzard/bc2007/index.html, accessed on 16 July 2008

[4]   Schroder, M. and Hunecke, A., “MARY TTS Participation in the Blizzard Challenge 2007”, in The Blizzard Challenge 2007, Bonn, Germany. Online: http://festvox.org/blizzard/bc2007/ index.html, accessed on 16 July 2008

[5]   Zhen-Hua Ling, Long Qin, Heng Lu, Yu Gao, Li-Rong Dai, Ren-Hua Wang, Yuan Jiang, Zhi-Wei Zhao, Jin-Hui Yang, Jie Chen, Guo-Ping Hu, “The USTC and iFlytek Speech Synthesis Systems for Blizzard Challenge 2007”, in The Blizzard Challenge 2007, Bonn, Germany. Online: http://festvox.org/ blizzard/bc2007/index.html, accessed on 16 July 2008

 


 

 

Blizzard Challenge 2009

CircumReality text-to-speech, a talking speech recognizer

Mike Rozak

mXac, Darwin, NT, Australia

 

Mike@mXac.com.au, http://www.CircumReality.com

Abstract

The CircumReality text-to-speech engine’s mean opinion (MOS) and similarity-to-original scores have improved significantly over the last three Blizzard Challenges [1] [2]. MOS has increased from 1.3 in 2007 to 2.8 in 2009. This paper describes the algorithmic improvements made to the CircumReality engine between the 2008 and 2009 Blizzard Challenges. The most significant improvements stemmed from a shift in the underlying philosophy of the engine: integrating automatic speech recognition (ASR) into the speech synthesis engine and creating a “talking speech recognizer”.

 

Index Terms: speech synthesis, speech recognition, games, unit selection, Blizzard Challenge

Introduction

The annual Blizzard Challenge allows text-to-speech researchers to compare their engine technologies against one another by providing a common speech database from which all entered voices are created [3]. Such a test allows speech researchers to eliminate voice-database quality, and to a lesser extent, hand-tuning, as factors in synthesized voice quality. Researchers employ the test’s results, including associated Blizzard-Challenge papers explaining other entrants’ results, to improve their engines.

The CircumReality text-to-speech engine has improved significantly over the three years that it has been entered in the Blizzard Challenge. (See figure 1.)

 

Figure 1: CircumReality text-to-speech engine similarity and mean-opinion scores (MOS) over three Blizzard Challenges.

 

The improvements to CircumReality’s speech quality came from two directions:

·         Acoustic features – The use of TD-PSOLA proved superior to time-domain stretched PCM (in the 2008 entry [2]) or additive sine-wave synthesis (in the 2007 entry [1]).

·         Using ASR for target and join costs – A new philosophical foundation for the text-to-speech engine was employed: The process of speech synthesis was understood in terms of a “talking speech recognizer”, and ASR (automatic speech recognition) was tightly incorporated into the synthesis process.

 

Blizzard Challenge 2009 results compared to 2008

CircumReality was entered in the 2009 Blizzard Challenge as voice “H”. The CircumReality engine was entered for the EH1 (10 hours of speech data), EH2 (1 hour of Arctic speech data) and ES1 (100 sentences from the Arctic dataset) tests. A Mandarin Chinese voice was generated, but not entered due to the CircumReality engine’s poor synthesis of Chinese.

The CircumReality engine uses unit-selection synthesis; unlike standard unit-selection synthesizers [4, pp 475-493], CircumReality runs its own speech recognizer in tandem with voice generation and synthesis [2] to help determine which units to select. Voice creation is mostly automated, although for the Roger dataset, some sentence utterance groups were manually eliminated from the prosody model because they contained atypical prosody.

In 2008, the CircumReality engine ranked at the bottom of the submitted entries [2]. The engine performed significantly better in the 2009 Blizzard Challenge, achieving better-than-average MOS and similarity scores. (See figure 2.) Interestingly, the engine didn’t do well on the word-error-rate test, as will be discussed later.

 

 

 

Figure 2: Mean opinion score (MOS) for EH1, EH2, and ES1. “Orig” is the original voice, “Circum” is CircumReality. “Fest”, “HTS 2007”, and “HTS 2005” are reference engines.

 

 

 

A talking speech recognizer

Stereotypical unit-selection synthesizers employ ASR for phoneme segmentation [4, pp 467-471], and may use the ASR phoneme scores, along with extremes in F0, energy, and duration, to eliminate the “worst” sounding phonemes.

From early in its development, the CircumReality text-to-speech engine has incorporated units’ ASR scores in the unit-select Viterbi search. [1]

CircumReality’s 2008 engine (CR2008) used ASR extensively. [2] Most notably, the target cost for F0 and duration mismatches were calculated using ASR; target costs no longer needed to be manually generated. Target costs for mismatched start/end of a word, and mismatched left/right contexts were also calculated using ASR. Unit join costs were calculated using the same feature-comparison algorithms employed by ASR.

CircumReality’s 2009 entry (CR2009) integrated ASR even more extensively than CR2008. ASR is so intertwined into the text-to-speech algorithms that CircumReality’s synthesis and ASR are inseparable.

All values used in the unit-selection Viterbi search come directly from, or are derived from ASR. Many values in the prosody model also originate from ASR.

In other words: CircumReality’s built-in ASR “listens to” and fine-tunes the output of the text-to-speech algorithms before they’re heard by the listener.

Significant engine changes between CR2008 and CR2009 that affected the 2009 Blizzard Challenge are:

 

·         Phoneme categories – CR2008 grouped phonemes into four categories when using ASR to calculate target costs: voiced plosive, unvoiced plosive, voiced non-plosive, and unvoiced non-plosive. [2] CR2009 increased the number of categories to sixteen, resulting in more accurate target cost calculations.

·         Half-phone target cost base – CR2008 and CR2009 synthesize using half units. CR2008 incorporated the unit’s context-dependent ASR score as the base value for the unit-selection score, using the same value for both the left and right halves of the unit. CR2009 uses ASR to calculate unique values for the left and right halves of the unit.

·         Explicitly calculated left/right context mismatches – In CR2008, if a unit with mismatched left/right phoneme context was used, a score penalty (calculated using ASR) would be applied. CR2009 still does this, in most cases. However, CR2009 identifies units with mismatched left/right contexts that are likely substitutes, and then explicitly calculates the unit-substitution’s score against the ASR context-dependent phoneme model of the desired unit. Doing so eliminates the need to use the estimated mismatch score penalty, producing a more-accurate target cost for likely substitutions.

·         Join cost calculation using a triangular window – In CR2008, join costs were calculated using an impulse window, using ASR to compare frames to the immediate left and right of the join. [2] CR2009 uses a triangular window with a width of around half a phoneme.

·         Target and estimated join costs calculated per voice – CR2008’s target and estimated join costs were calculated from 10 hours of recordings of my own voice. CR2009 calculates the target and estimated join costs from the Roger voice, improving target cost accuracy when synthesizing the Roger voice. The resulting target-cost values are significantly different to those derived from my own voice.

·         TD-PSOLA target costs – It is well known that TD-PSOLA distorts the original signal, and “as a rule of thumb” [4, pp 416] can only be used to double or halve the duration of a unit, and increase or decrease its F0 by half an octave. CR2009 used ASR to determine how much changing the duration and/or F0 using TD-PSOLA affected the speech quality, and included this into the target cost. I won’t detail the CR2009 algorithm to derive TD-PSOLA costs because recently-improved algorithms (see below) have made the TD-PSOLA target-cost algorithm employed in CR2009 obsolete.

·         “Snap to” F0 and duration affected by target costs – Stereotypical unit-selection synthesizers maintain the original units’ F0 and duration. Due to transplanted prosody requirements for games, the CircumReality engine modifies F0 and duration using TD-PSOLA. To minimize the signal distortions created by TD-PSOLA, CR2008 and CR2009 adjust the F0 and duration of a unit away from the values requested by the prosody model, and towards the F0 and duration of the original unit. This approach reduces prosody quality to improve acoustic quality.  In CR2009, the amount of adjustment is controlled by the target-cost penalty per octave shift of F0 or doubling of duration. For example: Phoneme groups that have higher F0 target-cost penalties, meaning that they don’t sound as good when pitch shifted using TD-PSOLA, have their F0 weighted more towards the unit’s original F0.

·         TD-PSOLA – CR2008 synthesized audio using time-domain stretched PCM, causing audible artifacts when F0 was modified even slightly. [2] CR2009 used TD-PSOLA, resulting in improved acoustic synthesis.

·         Prosody model – The ASR-calculated F0, duration, and energy target costs are employed by the prosody model to estimate how perceptible altering a syllable’s F0, duration, or energy is. Such values are only a guestimate, to be used until better approaches for calculating prosody-specific F0, duration, and energy target costs can be devised.

 

Other significant improvements to CR2009 didn’t affect the 2009 Blizzard Challenge:

 

·         Small voices – The CR2008 and CR2009 Blizzard-Challenge voices used around 350,000 units and several gigabytes on disk. Text-to-speech voices for games must be smaller, around 8000 units and 30 megabytes. CR2008 eliminated units by retaining the 8000 best-scoring, most-commonly-used phoneme sequences, based on an average of the unit’s ASR-generated base score. CR2009 includes estimated join costs for non-contiguous units between the first two and last two phonemes of the sequences, also calculated by ASR. For example: Estimated join costs around the phoneme “t” are always high, due to coarticulation. Conversely, “m” has low estimated join costs. As a result, triphone sequences with “t” occurring in the middle of sequence are more likely to be included in the 8000-unit voice than triphone sequences with an “m” in the middle.

·         Randomly generate several sentences and select the “best sounding” one – CR2009 can randomly generate several different prosodies for a given sentence, along with randomly selected alternative pronunciations from the lexicon. All variations are synthesized, and the synthesized sentence with the best unit-selection score is spoken. This technique was not used for the Blizzard Challenge 2009 because it proved to be too slow, and produce only a marginal improvement in speech quality.

·         TD-PSOLA target cost improvements – After submitting the synthesized results for Blizzard Challenge 2009, further improvements were made to calculating TD-PSOLA target costs. The CircumReality engine now uses pitch-detection confidence scores from the training data to adjust the “per octave shift” and “per duration doubling” target costs of TD-PSOLA. In general, the higher the pitch-detect confidence, the lower the TD-PSOLA target cost. ASR is used to automatically calculate the TD-PSOLA per-octave/duration target costs as a function of pitch-detection confidence.

 

 

Conclusions and future work

TD-PSOLA and the “talking speech recognizer” philosophy significantly improved CircumReality’s MOS and similarity scores.

However, CircumReality’s 2009 “word error rate” was still very high. (See figure 3.)

 

Figure 3: Word error rate

 

The high word-error rate is unexpected, and needs to be explored. Several possible causes for the word-error rate will be investigated:

 

·         The ASR algorithm may not be accurate enough – CircumReality’s ASR algorithms haven’t yet been tested for accuracy. Future plans involve writing a phoneme-based ASR accuracy test, and then fine-tuning constants and algorithms to improve ASR accuracy.

·         Join cost triangle window size – Join costs are calculated by comparing the boundaries of a join using a triangle window of approximately half a phoneme. Since the units of the beam search are “frame comparison error * time”, a shorter-duration window causes the join cost to affect the beam search more. Thus, a shorter-duration triangle window encourages more non-contiguous units. The word-error-rate listening test was based on short, confusable word pairs, implying that a longer triangle-window would encourage contiguous units and reduce the word error rate.

·         Join cost vs. target cost weight – In CR2009, the join cost score is combined with the target cost score using a weight of 1.0. The reasoning for using 1.0 may need to be re-examined; a different weight might make more logical sense. Increasing the join cost weight would encourage contiguous units.

 

The CircumReality text-to-speech engine was created for the CircumReality game [5], and all work on the engine is done with game development in mind. The 2009 Blizzard Challenge has provided some information relevant to game development:

 

·         Minimizing voice-recording costs – While 10,000 sentences for each voice would be ideal, recording so many sentences isn’t possible on a small financial budget. 1000 sentences appear to be the minimum number of recordings needed before MOS declines dramatically. (See figure 2.) CircumReality’s low MOS for ES1 (generated from 100 sentences) illustrates the rapid drop-off in quality resulting from less data. Due to the CircumReality game’s low budget, most voice data will come from free public sources where 1-hour voiced databases are common, but 10-hour voice databases are rare.

·         Quality vs. quantity – Another voice-design tradeoff is whether the game should ship with a couple of large voices generated from 10 hours of speech, and then use extensive voice transformations to create voices for one hundred characters, or to ship with 20-40 smaller voices and employ only minor voice transformation to cover the one hundred characters. HMM synthesis using highly-parameterized speech audio would enable both significant voice transformations and small voices, with HTS 2007’s ES1 matching its EH1 and EH2 scores. (See figure 2.) But, from the Blizzard Challenge 2008’s overall results, it is obvious that “the best” concatenative PSOLA synthesizers still have a significantly higher MOS for EH2 (small voices) than “the best” parameterized-speech HMM synthesizers have for EH1. These results show that 20-40 smaller PSOLA voices will produce a better over-all MOS than highly-parameterized voices.

·         Prosody – Listening to the restaurant query-responses sub-test of the 2009 Blizzard Challenge clearly demonstrated how poor CR2009’s prosody was. Unfortunately, separate test results weren’t provided, so no numerical comparison is possible; I suspect CircumReality’s MOS would be relatively higher (compared to other entrants) if the restaurant-query test results were removed. However, better prosody is not that critical for games. Long sentences such as those used in the restaurant-query sub-test don’t appear often in games; players get bored listening to even medium-length sentences. Furthermore, half of the sentences that are spoken during gameplay can be “prerecorded” with transplanted prosody, overriding the lower-quality synthesized prosody.

·         Expert speech listener bias – “Speech experts” consistently gave all entrants the same or higher MOS and similarity scores. In terms of gameplay, this implies that players will “grow accustomed to” text-to-speech voices over time. (See figure 4.)

Figure 4: Expert speech listener bias

 

 

 

References

[1]   Rozak, M., “Text-to-speech Designed for a Massively Multiplayer Online Role-Playing Game (MMORPG)”, in The Blizzard Challenge 2007, Bonn, Germany. mXac. Online: http://festvox.org/blizzard/bc2007/index.html, accessed on 19 July 2009.

[2]   Rozak, M., “CircumReality functionality delta: Blizzard Challenge 2007 to 2008”, in The Blizzard Challenge 2008, Brisbane, Australia. mXac. Online: http://festvox.org/blizzard/ blizzard2008.html, accessed on 19 July 2009.

[3]   Karaiskos, V., King, S., Clark, R., Mayo, C., “The Blizzard Challenge 2008”, in The Blizzard Challenge 2008, Brisbane, Australia. University of Edinburgh. Online: http://festvox.org/ blizzard/blizzard2008.html, accessed on 19 July 2009.

[4]   Taylor, P., Text-to-Speech Synthesis, 2009, New York, Cambridge University Press.

[5]                  Rozak, M., “What is CircumReality?”, mXac. Online: http://www.CircumReality.com, accessed on 19 July 2009.

 


 

 

Source-code

 

I have suspended work on my www.CircumReality.com graphical-MUD. (Multiplayer interactive-fiction game.)

A 1.5-gigabyte download of the source-code is available for free, from this web-site, http://www.CircumReality.com/mXacSourceCode.zip.

A piece-wise download of the .zip file is available at:

1.       http://www.CircumReality.com/mXacSourceCode_part1.zip

2.       http://www.CircumReality.com/mXacSourceCode_part2.zip

3.       http://www.CircumReality.com/mXacSourceCode_part3.zip

4.       http://www.CircumReality.com/mXacSourceCode_part4.zip

5.       http://www.CircumReality.com/mXacSourceCode_part5.zip

6.       http://www.CircumReality.com/mXacSourceCode_part6.zip

7.       http://www.CircumReality.com/mXacSourceCode_part7.zip

8.       http://www.CircumReality.com/mXacSourceCode_part8.zip

9.       http://www.CircumReality.com/mXacSourceCode_part9.zip

 

The .zip file includes source-code for:

·         Text-to-speech – As described in my Blizzard papers. (http://www.synsig.org/index.php/Blizzard_Challenge)

 

Free source-code is also available for the “Festival” text-to-speech engine. Just search for it on the internet. (http://festvox.org/festival/, http://www.cstr.ed.ac.uk/projects/festival/)

 

·         A multi-core 3D renderer

 

·         A 3D editor.

 

To work-around a “This is only a beta” limiter, just set your computer’s date-time year to 2007.

 

·         A graphical MUD client (Multiplayer interactive-fiction client)

 

·         Multiplayer interactive-fiction server – An LPMUD-like server with an IDE (integrated development-environment). (http://en.wikipedia.org/wiki/LPMud)

 

The MUD scripting-code is targeted less-towards combat, and more towards interactive-fiction and NPC-interaction.

 

You should look at the built-in NPC artificial-personality scripting-code (sometimes called “artificial-intelligence”), visible through the IDE (integrated development environment).

 


 

Multiplayer Interactive-Fiction Scripting-Code

Overview

This is ALL (hopefully) of the script-code for my multiplayer interactive-fiction game. You can look at this code to come-up with ideas for how to make more interesting non-player characters (NPCs) in the virtual-world or adventure games.

If I am missing anything (which I may be), then look through my source-code or the scripting source-code (available through the CircumReality Integrated Development Environment). (BUGBUG – Download link)

 

 

Documentation

Documentation – Standard Library

#importfunc

Used to import functions and methods from C++ code.

<p>

    A function or method can "import" code from a C/C++ library.

    To do this it uses the following code:

</p>

 

<p align=center><table><tr><td><font face=courier>

    #importfunc <italic>FuncName</italic>

</font></td></tr></table></p>

 

<p>

    "FuncName" is replaced by a function name that is supported

    by the C/C++ application using MIFL.

</p>

0.1  “Hello world” Application

Steps you through the process of writing a "Hello world" application.

<p><bold><big>Comparing MIFL to other languages</big></bold></p>

 

<p>

    MIFL is similar to <bold>C, C++, Java, and Flash's Actionscript</bold>.

    If you've used either of these before then you'll find it

    very easy to pick up. If you have used other programming

    languages, such as Visual Basic, Python, Inform, or TADs then

    MIFL will take a bit more getting use to, although it's

    still just a programming language.

</p>

 

<p>

    If you haven't done programming before then I suggest you get

    a good book on programming C, C++, Java, or Flash and learn

    from that. These tutorials may not be detailed enough to

    help you learn how to program from scratch; you're welcome

    to try though.

</p>

 

 

 

<p/><p><bold><big>Libraries</big></bold></p>

 

<p>

    A MIFL program (also called a project) is pieced together

    using one or more <bold>libraries</bold>. A library is a

    collection of code (and documentation) that the programmer

    things fits together. Each library is one file saved on disk.

</p>

 

<p>

    Building a program from a collection of libaries rather than

    one monolithic file has advantages; it allows the programmer to

    share code between different projects.

</p>

 

<p>

    For example: If a programmer writes a lot of code to do fancy

    math (like Fourier Transforms) then he/she could put that in

    it's own library, separate from the libary for the application-specific

    code. Then, if the programmer (or someone else) ever needs

    Fourier Transforms in another program, he/she just has to

    include the Fourier Transform library in the new program.

</p>

 

 

<p>

    A MIFL program (project) will contain a minimum of two libraries:

</p>

 

<ul>

    <li>

           <bold>Standard library</bold> - This library contains necessary

           functions, globals, method definitions, and property definitions

           for using MIFL. It should <bold>always</bold> be part of the

           project. (Unless you're a really advanced user and know what you're

           doing.)

    </li>

    <li>

           <bold>Context-specific library</bold> - This library defines

           functions, etc. necessary for the context of the application.

           For example: If you're using MIFL for an interactive fiction

           application, then an "Interactive Fiction Library" that's common

           to all IF applications will be needed.

    </li>

    <li>

           <bold>Application specific library</bold> - This is one or more

           libraries specific to your program.

           For example: If you're using MIFL for IF, this would be the

           code for the IF's rooms, items, and NPCs.

    </li>

</ul>

 

<p>

    To set up the libraries you need for the "Hello world" application:

</p>

 

<ol>

    <li>

           Click on the <bold>Add new libraries</bold> menu item under

           the <bold>Libraries</bold> menu.

    </li>

    <li>

           In the "Add a new library" page, look for the "Standard library" under

           "Built in libraries". If it's not there (which it shouldn't be) then you

           already have the standard library included (automatically) in your

           project. If the button for "Standard library" exists then <bold>press it</bold> to

           include the library.

    </li>

    <li>

           You will also need a library to store the code that displays "Hello world!".

           To do this, press <bold>"New library file"</bold> in the "Add a new library" page.

    </li>

    <li>

           In the dialog asking for the file name, <bold>type in a name</bold>, such

           as "HelloWorld.mfl".

    </li>

    <li>

           Once you've added the file, you should see the center title of the

           page change to "HelloWorld.mfl" (in yellow text) to indicate that any

           changes you make affect that library. If it doesn't, then

           select <bold>HelloWorld.mfl</bold> from the <bold>Libraries</bold> menu.

    </li>

</ol>

 

<p/><p><bold><big>Functions</big></bold></p>

 

<p>

    The next thing you need to do is create a <bold>function</bold>. In C, C++, or

    Java you type in code like "int Function (int a, double b) {}" to declare

    a function. It's much easier to create a function in MIFL.

</p>

 

<ol>

    <li>

           Select the <bold>"Add a new function"</bold> menu item

           underneath the <bold>"Functions"</bold> menu.

    </li>

    <p>

           A new function will be created and the "Modify function" page will be

           displayed so you can edit it.

    </p>

    <li>

           In the "Modify function" page, type in a new <bold>"Name"</bold> for

           the function; change it to "HelloWorld".

    </li>

    <p>

           If you don't see any entry for "Name" then press

           the <bold>"Description"</bold> tab in the menu.

    </p>

    <p>

           Function names (and object, method, variable, etc. names) <bold>cannot

           contain any spaces.</bold> They can only contain letters, numbers (but not

           the first character) and underscores.

    </p>

    <li>

           While you're at it, you can also <bold>type in</bold> a one-line

           description, like "Displays Hello World!", and even a longer multi-line

           description. The descriptions and help categories are used like

           comments in front of functions. They are also automatically

           included in the help system; all you need to do to see them is

           press the <bold>"Rebuild help"</bold> button at the bottom of the help

           page.

    </li>

</ol>

 

<p>

    When you are modifying a function (or object or method) the menu

    will be extended to include "Description", "Parameters",

    "Code", and "Misc." Pressing these switches to different

    sub-pages in the function.

</p>

 

<ul>

    <li>

           <bold>Description</bold> - Lets you modify the function's

           name, description, and where it appears in help.

    </li>

    <li>

           <bold>Parameters</bold> - Use this to control what parameters

           are passed into the function and what it returns.

    </li>

    <li>

           <bold>Code</bold> - This page lets you write the code and

           test it.

    </li>

    <li>

           <bold>Misc.</bold> - Miscellaneous options, such as moving

           the function to a different library.

    </li>

</ul>

 

 

<p/><p><bold><big>Writing code</big></bold></p>

 

<p>

    To actually write the code for the "HelloWorld"

    function, press the <bold>"Code"</bold> tab. The page will

    change to show you an edit box for the code.

</p>

 

<p>

    Type in:

</p>

 

<p align=center><table><tr><td><font face=courier>

    Trace ("Hello world!");

</font></td></tr></table></p>

 

<p>

    Your "Hello world" program is written. All it does

    is pass the string, "Hello world!", into the Trace() function.

    The Trace() function (from the "Standard Library") just

    writes the string to the debug log. You'll see this in a moment.

</p>

 

<p>

    To verify that you haven't made any typing mistakes,

    press the <bold>"Test compile this function"</bold> button.

    The menu will be extended to show you any errors or warnings;

    clicking on one an error or warning will highlight the code

    where the error/warning occurred.

</p>

 

 

 

 

<p/><p><bold><big>Testing your application</big></bold></p>

 

<p>

    To test your application:

</p>

 

<ol>

    <li>

           Select the <bold>Compile</bold> menu item under

           the <bold>Misc</bold> menu. This is different than the

           "Test compile this function" option in the code window

           because it compiles all the functions and objects, as

           opposed to just the one you're working on.

    </li>

    <p>

           If there were any errors then fix them.

    </p>

    <li>

           When you're finished with the errors press

           the <bold>"Hide errors"</bold> link.

    </li>

    <li>

           Select the <bold>"Test compiled code"</bold> menu

           item under <bold>"Misc"</bold>.

    </li>

</ol>

 

<p>

    The "Test compiled code" page will appear. Its window is

    divided into several panes:

</p>

 

<ul>

    <li>

           <bold>Right</bold> - The right side of the page lists

           all the objects, global variables, etc. running in the

           program. Clicking on "Functions" will expand the list

           to show all the functions; your "HelloWorld" function

           will be among them.

    </li>

    <li>

           <bold>Left, top</bold> - The single-line edit box lets

           you type in code to run. You'll be using this in a minute.

    </li>

    <li>

           <bold>Left, center</bold> - The "Watch variables" section

           shows you the values of variables in globals, objects, and

           within a function (while you're debugging). For example:

           One variable included in the Standard Library is "Pi";

           if you type "Pi" into one of the fields you'll see

           "3.14159..." appear next to it.

    </li>

    <li>

           <bold>Left, bottom</bold> - The "Debug trace" shows you

           any run-time errors and anything passed into

           the <bold>Trace()</bold> function call, which HelloWorld()

           uses.

    </li>

</ul>

 

<p>

    To run your HelloWorld function:

</p>

 

<ol>

    <li>

           <bold>Type</bold> "HelloWorld()" into the single-line

           edit field at the top.

    </li>

    <li>

           Press <bold>"Run this"</bold>. (Directly under the field.)

    </li>

</ol>

 

<p>

    That's it, your program has been run. You can see the

    results in the "Debug trace" window below. It should

    contain:

</p>

 

 

<p align=center><table><tr><td><font face=courier>

    Execute code: "HelloWorld()"<br/>

    Hello world!Returns: undefined

</font></td></tr></table></p>

 

<p>

    The "Hello world!" on the second line is the output of the

    Trace() call. "Returns: undefined" tells you what the

    function returned, which was nothing.

</p>

 

 

 

 

 

 

<p/><p><bold><big>Comments</big></bold></p>

 

<p>

    I haven't used any comments in my sample code, but

    rest assured, MIFL allows for comments. There are two types,

    and act just like comments in C, C++, Java, and ActionScript:

</p>

 

<ul>

    <li>

           <bold>// comment</bold> - If you use a double-slash then

           all the text to the end of the line will be ignored by

           the compiler.

    </li>

    <li>

           <bold>/* comment */</bold> - If you use a /* then

           all the text until the next */ will be ignored by

           the compiler.

    </li>

</ul>

 

 

0.2  Variables and operations

Discusses how variables and operations work.

 

<p><bold><big>Variables</big></bold></p>

 

<p>

    MIFL variables behave almost exactly like those in Flash's

    ActionScript because they are "un-typed".

    They are very different to variables in Java, C, and C++,

    which are "typed".

</p>

 

<p>

    In MIFL, a variale it a storage slot for data that can

    store <bold>any</bold> type of data. MIFL handles several

    data types. The important ones are:

</p>

 

<ul>

    <li>

           <bold>Number</bold> - This is a floating point number.

           For those of you familiar with C, it's a "double".

           (Example: 123.435)

    </li>

    <li>

           <bold>Boolean</bold> - This can either

           be <bold>"True"</bold> or <bold>"False"</bold>.

    </li>

    <li>

           <bold>Character</bold> - A single character, such as

           'a' or '!'. Characters are identified by the

           single-quotes (') surrounding it.

    </li>

    <li>

           <bold>Null</bold> - This can only be one

           value, <bold>Null</bold>. It represents an empty value.

    </li>

    <li>

           <bold>Undefined</bold> - This can only be one

           value, <bold>Undefined</bold>. Undefined implies that the

           value is not only empty, but it wasn't even initialized.

    </li>

    <li>

           <bold>String</bold> - A series of characters.

           Strings are identified by the quotes (") surrounding them.

           (Example: "Hello world!")

    </li>

    <li>

           <bold>List</bold> - A list if an array of variables.

           Lists are identified by the brackets ([ ]) that surround it.

           (Example: [1, 3, "hi there", true] is a list with the 1st

           element contain the number 1. 2nd is 3. 3rd is the

           string "hi there", and 4th is the boolean, true.

    </li>

    <li>

           <bold>Object</bold> - This is reference to an object.

           I'll explain this in detail when I get into objects.

    </li>

    <li>

           <bold>Function</bold> - A reference to a function.

           In C or C++ this is equivalent to a pointer to a function.

    </li>

    <li>

           <bold>Method</bold> - A reference to a method within an object.

           In C or C++ this is equivalent to a pointer to a method.

    </li>

    <li>

           <bold>String table entry</bold> - I'll discuss this later.

    </li>

    <li>

           <bold>Resource</bold> - I'll discuss this later.

    </li>

</ul>

 

<p>

    Unlike C, C++, and Java, a variable can <bold>change data types</bold> at

    run-time. So, even if "x" were initialized to 54.45, it

    could be changed to "Hi there" later one.

</p>

 

 

 

<p/><p><bold><big>Using variables in functions</big></bold></p>

 

<p>

    Creating a variable in a function is easy. In your

    HelloWord() code, just add the following line:

</p>

 

<p align=center><table><tr><td><font face=courier>

    var x = 54.3534;<br/>

    Trace (x);

</font></td></tr></table></p>

 

<p>

    If you then <bold>Run this</bold> the "Debug trace" will

    also display "54.3534".

</p>

 

<p>

    (Actually, it probably won't do this unless you first

    run <bold>"Compile"</bold> under <bold>"Misc."</bold> again.)

</p>

 

<p>

    In C or C++ you would have typed in "double x = 54.3534" or

    "int x = 53" or "char x[] = "hello there!"" because variables

    are "typed". You don't need to do this in MIFL. It's

    just "var x = 54.3534", "var x = 53", or "var x = "hello there!"".

</p>

 

<p>

    As with C, C++, Java, and ActionScript, you can <bold>also create

    several variables at once</bold> using "var x,y,z;" or

    "var x=43, y="hi there", z=true;".

</p>

 

<p>

    <bold>Note:</bold> All names (including variables)

    are case <bold>insensative</bold> in MIFL. That means that

    varible "x" is equivalent to "X", and functon "HelloWorld"

    is equivalent to "helloWORLD". C, C++, and Java work

    just the opposite, being case sensative, and differentiating

    between HelloWorld() and helloWORLD().

</p>

 

 

 

<p/><p><bold><big>Operators</big></bold></p>

 

<p>

    Once you have variables, you'll want to do operations on them,

    such as addition and assignment. The operators in MIFL are

    almost identical to C, C++, Java, and ActionScript. They are (in

    order of precedence):

</p>

 

<table width=100%>

    <tr>

           <td width=33%><bold>.</bold></td>

           <td width=66%>Object property or method access, like "object.property".</td>

    </tr>

    <tr>

           <td width=33%><bold>[ ]</bold></td>

           <td width=66%>Creating a list or indexing into a list or string.</td>

    </tr>

    <tr>

           <td width=33%><bold>( )</bold></td>

           <td width=66%>Parenthesis or parameters for a function call.</td>

    </tr>

 

    <tr><td/></tr>

    <tr>

           <td width=33%><bold>++ --</bold></td>

           <td width=66%>

                  Increment or decrement a variable. If ++ (or --) is to the right of

                  the variable the variable's value is put on the stack and then incremented/decremented.

                  If ++ (or --) is to the left, the variable is first incremented or

                  decremented, and then the variable's value is put on the stack.

           </td>

    </tr>

    <tr>

           <td width=33%><bold>-</bold></td>

           <td width=66%>Negation, such as in "-(x+y)"</td>

    </tr>

    <tr>

           <td width=33%><bold>~</bold></td>

           <td width=66%>Bitwise not.</td>

    </tr>

    <tr>

           <td width=33%><bold>!</bold></td>

           <td width=66%>Logical not.</td>

    </tr>

 

    <tr><td/></tr>

    <tr>

           <td width=33%><bold>* / %</bold></td>

           <td width=66%>Multiply, divide, and modulo.</td>

    </tr>

 

    <tr><td/></tr>

    <tr>

           <td width=33%><bold>+ -</bold></td>

           <td width=66%>Addition and subtraction.</td>

    </tr>

 

    <tr><td/></tr>

    <tr>

           <td width=33%><bold>&lt;&lt; &gt;&gt;</bold></td>

           <td width=66%>Bitwise shift left or right.</td>

    </tr>

 

    <tr><td/></tr>

    <tr>

           <td width=33%><bold>&lt; &lt;= &gt; &gt;=</bold></td>

           <td width=66%>Less than, less than or equal, greater than, greater than or equal</td>

    </tr>

 

    <tr><td/></tr>

    <tr>

           <td width=33%><bold>== !=</bold></td>

           <td width=66%>Equality or not equality</td>

    </tr>

    <tr>

           <td width=33%><bold>=== !==</bold></td>

           <td width=66%>

                  Strict equality or strict not equality. These <bold>do not</bold> appear

                  in C, C++, or Java. They test equality but are strict about it.

                  Because of automatic conversions between numbers and strings,

                  43 == "43" <bold>but</bold> 43 !== "43", although 43 === 43. Strict equality does not

                  do automatic conversions.

           </td>

    </tr>

 

    <tr><td/></tr>

    <tr>

           <td width=33%><bold>&amp;</bold></td>

           <td width=66%>Bitwise AND</td>

    </tr>

 

    <tr><td/></tr>

    <tr>

           <td width=33%><bold>^</bold></td>

           <td width=66%>Bitwise XOR</td>

    </tr>

 

    <tr><td/></tr>

    <tr>

           <td width=33%><bold>|</bold></td>

           <td width=66%>Bitwise OR</td>

    </tr>

 

    <tr><td/></tr>

    <tr>

           <td width=33%><bold>&amp;&amp;</bold></td>

           <td width=66%>Logical AND</td>

    </tr>

 

    <tr><td/></tr>

    <tr>

           <td width=33%><bold>||</bold></td>

           <td width=66%>Logical OR</td>

    </tr>

 

    <tr><td/></tr>

    <tr>

           <td width=33%><bold>? :</bold></td>

           <td width=66%>

                  Contional. Example: "(1 &lt; 2) ? 5 : 3" will return "5".

           </td>

    </tr>

 

    <tr><td/></tr>

    <tr>

           <td width=33%><bold>= += -= *= /= %= &lt;&lt;= &gt;&gt;= &amp;= ^= |=</bold></td>

           <td width=66%>Various forms of assignment</td>

    </tr>

 

    <tr><td/></tr>

    <tr>

           <td width=33%><bold>,</bold></td>

           <td width=66%>Comma. Separates parameters, list elements, and (sometimes) lines of code.</td>

    </tr>

</table>

<p/>

 

<p>

    You can combine variables and operators to make your

    HelloWorld() function do some calculations. Try tying

    in and then running:

</p>

 

<p align=center><table><tr><td><font face=courier>

    var x = 18;<br/>

    var y = 6;<br/>

    var z = x * y + 89;<br/>

    z *= 2;<br/>

    Trace (z);

</font></td></tr></table></p>

 

 

<p/><p><bold><big>Debugging</big></bold></p>

 

<p>

    MIFL includes a debugger that allows you to step through the

    code and see the flow of control along what variables

    are at each step of the execution.

</p>

 

<p>

    To test this:

</p>

 

<ol>

    <li>

           <bold>Type in</bold> the code (as per above).

    </li>

    <li>

           Select <bold>"Compile"</bold> underneath

           the <bold>"Misc."</bold> menu.

    </li>

    <li>

           Select <bold>"Test compiled code"</bold> underneath

           the <bold>"Misc."</bold> menu.

    </li>

    <li>

           In the "Test compiled code" page, check

           the <bold>"Debug"</bold> checkbox.

    </li>

    <li>

           Type in <bold>"HelloWorld()"</bold> and

           press <bold>"Run this"</bold>.

    </li>

    <li>

           The "Debug" page will be displayed with "HelloWorld()" being

           highlighted. Press <bold>"Step in"</bold> to step in.

    </li>

    <p>

           The Debug page will not display the code from the HelloWorld()

           function.

    </p>

    <li>

           To see the values of the variables, x, y, and z, type

           in <bold>"x", "y", and "z"</bold> (each on their own line)

           into the "Watch variables" list. At first each will

           be "undefined" since they haven't been set.

    </li>

    <li>

           Press <bold>"Step"</bold> to step to the next line of code.

           If, for example, you stepped over the "x = 18;" line then

           you'll see the watched variable, x, change to "18".

    </li>

    <li>

           Keep on pressing <bold>"Step"</bold> until the code finishes

           executing. You can watch how the variables change with each

           step.

    </li>

    <p>

           If your function called another function (other than Trace()),

           you could press <bold>"Step in"</bold> to debug that function.

           (It's not worth stepping into Trace() because it just ends up

           calling a built-in function, which can't be debugged.)

    </p>

    <p>

           You can also press <bold>"Run"</bold> to run the code without

           stepping, or <bold>"Exit"</bold> to stop running the code.

    </p>

</ol>

 

 

 

<p/><p><bold><big>Passing parameters to functions</big></bold></p>

 

<p>

    Just like other languages, you can pass parameters to functions.

    To do this:

</p>

 

<ol>

    <li>

           Select <bold>"HelloWorld"</bold> under

           the <bold>"Functions"</bold> menu.

    </li>

    <li>

           Press the <bold>"Parameters"</bold> tab in the "Modify

           function" page.

    </li>

    <li>

           Type in a parameter name, <bold>"Input"</bold> and

           a description for the meaning of the parameter.

    </li>

    <p>

           If you want more than one parameter, just

           press <bold>"Add more parameter slots"</bold> to add another

           slot. When you fill that one in, you can press "Add more

           parameter slots" again to add more.

    </p>

    <li>

           Type in a short desciption for the <bold>Return value</bold> of

           HelloWorld(), such as "Does complicated math on Input and then

           returns that."

    </li>

    <li>

           Switch to the <bold>"Code"</bold> tab.

    </li>

    <li>

           <bold>Type in</bold> the following code:

    </li>

    <p align=center><table><tr><td><font face=courier>

           var x = 18 + Input;<br/>

           var y = 6;<br/>

           var z = x * y + 89;<br/>

           z *= 2;<br/>

           Trace (z);<br/>

           return z;

    </font></td></tr></table></p>

    <li>

           <bold>Compile</bold> the project.

    </li>

    <li>

           Select the <bold>"Test compiled code"</bold> menu item.

    </li>

    <li>

           Type in <bold>"HelloWorld (53)"</bold> into the edit field,

           and then press <bold>"Run this"</bold>.

    </li>

    <p>

           If you have debuggin turned on you'll be able to watch the

           value of "Input", in addition to that of the variables,

           "x", "y", and "z".

    </p>

    <p>

           If you look at the debug log, you'll see that the Trace(z)

           outputs "1030" and then the return value is printed out

           as "Returns: 1030".

    </p>

</ol>

 

<p>

    In addition to the parameters you add to the function, MIFL

    functions (and methods) support two "hidden" parameters:

</p>

 

<ul>

    <li>

           <bold>Arguments</bold> - This is a list of all the parameters

           passed into the function. In the case of the HelloWorld()

           example it would be [53]. If more parameters were passed in

           the list would have more elements.

    </li>

    <li>

           <bold>This</bold> - When you run HelloWorld(), This will be

           set to <bold>Undefined</bold>. However, if HelloWorld() were

           a method in an object, This would contain a reference to the

           object. C++, Java, and ActionScript have a similar construct.

    </li>

</ul>

 

 

 

<p/><p><bold><big>Global variables</big></bold></p>

 

<p>

    Like other languages, MIFL supports "global variables" which

    are variables that can be accessed by any function or method, and

    which persist from one function call to the next.

</p>

 

 

<p>

    To create a global variable:

</p>

 

<ol>

    <li>

           Select the <bold>"Add new variable"</bold> menu item

           under the <bold>"Variables (global)"</bold> menu item.

    </li>

    <li>

           In the "Modify global variable" page that appears, type

           in a <bold>"Name"</bold> for the variable, such as "MyGlobal".

    </li>

    <li>

           Under "Initialized to", <bold>type in</bold> the value you'd

           like the global to being with, such as 54.456. If you leave

           this blank the global will initially be set to <bold>Undefined</bold>.

    </li>

    <p>

           You can also type in a short and long description, just like

           with the function.

    </p>

    <li>

           Re-<bold>Compile</bold> the project and return

           to <bold>Test compiled code</bold>.

    </li>

    <li>

           <bold>Type</bold> "MyGlobal" into the watch list to see

           it's value, which should be 54.456.

    </li>

    <p>

           You can also see the value of MyGlobal by clicking

           on the <bold>"Global variables..."</bold> link on the right

           side of the test window. This will display the list of all

           global variables. Hovering the mouse over the "MyGlobal" entry

           will show it's value in a tooltip.

    </p>

    <li>

           You can change the value of MyGlobal by writing some code

           into HelloWorld(), and then compiling and running it, or

           by <bold>typing</bold> "MyGlobal = 12345" in the test

           edit window and pressing <bold>"Run this"</bold>.

    </li>

</ol>

 

<p>

    <bold>Warning:</bold> A global variable (or object property)

    can initialize itself (with "initialized to") to use the contents of another global

    variable. This has two potential problems:

</p>

 

<ul>

    <li>

           <bold>Undefined</bold> - If the global variable that's being

           referenced has not yet been initialized due to the

           semi-arbitrary initialization errors of globals, then the

           global variable will be <bold>undefined</bold>. This problem

           won't happen when initializing an object's property to a global

           variable because globals are always initialized first.

    </li>

    <li>

           <bold>Fracturing</bold> - If a global variable or property is

           initialized by referencing a global variable which is in turn

           initialized by a string or list, then the new global variable

           or property will contain the correct string/list <bold>but</bold> the

           string/list will be different pieces of data.

    </li>

    <p>

           Hence, if

           global variable A is initialized to <bold>"Hello"</bold>, and global variable

           B is initialized to <bold>A</bold>, then global variable

           B will also be initialized to <bold>"Hello"</bold>.

           However, If A.StringAppend(" there") is called, A will

           be "Hello there", but B will still be "Hello".

    </p>

</ul>

 

 

 

 

 

0.3  Strings

Tutorial about how to use strings in MIFL.

 

<p><bold><big>Strings</big></bold></p>

 

<p>

    Strings and lists are similar to ActiveScript's strings and arrays.

    They are very different from strings in C and C++; C/C++ don't

    even contain lists.

</p>

 

<p>

    To create a string in code, just use quotes around some text,

    such as <bold>"Hello world!"</bold>.

</p>

 

 

 

<p/><p><bold><big>String operators</big></bold></p>

 

<p>

    Many of the operators work with strings:

</p>

 

<ul>

    <li>

           <bold>x = "Hello" + " there!"</bold> - This fills x

           with <bold>"Hello there!"</bold>, concatenating two or

           more strings together.

    </li>

    <li>

           <bold>x += "Hello"</bold> - This is interpreted as "x = x + "Hello"".

    </li>

    <li>

           <bold>"abacus" &lt; "zebra"</bold> - The &lt;, &lt;=,

           &gt; &gt;=, ==, and === operators do a case <bold>sensative</bold> comparison

           of the two strings. You need to use StringCompare() to do a case

           insensative compare.

    </li>

    <li>

           <bold>x = "Hello"[2]</bold> - Fills x in with the 3rd character (0 being the

           first character). x = 'l'.

    </li>

    <li>

           <bold>x = "Hello"; x[2] = 'a';</bold> - This fills x with "Hello",

           but then changes the 3rd character to 'a', resulting

           in "Healo". If the index is beyond the end of the string, the

           string will be extended with spaces.

    </li>

</ul>

 

<p>

    Depending upon the operator and the location of the string

    (whether it's to the left or right of the operator), strings will

    be converted to other data types (such as numbers) or other

    data types will be converted to strings.

</p>

 

<p>

    Some examples:

</p>

 

<ul>

    <li>

           <bold>x = "89" + 42;</bold> - The number is converted to a string.

           This results in the <bold>string</bold> "8942".

    </li>

    <li>

           <bold>x = 42 + "89";</bold> - The string is converted to a number.

           This results in the <bold>number</bold> 131.

    </li>

</ul>

 

<p>

    For more details on type conversions, see the reference section.

</p>

 

 

<p/><p><bold><big>String "methods"</big></bold></p>

 

<p>

    Strings support "methods", which are functions that automatically

    modify the string that they appear after. Example: To use the

    method "StringAppend" with the string in variable x,

    just use "x.StringAppend()".

</p>

 

<p>

    Below is a partial list of methods supported by strings.

    A complete list can be found under the "Reference" section of

    help, in "Data types", "Strings".

</p>

 

<ul>

    <li>

           <bold>StringAppend</bold> - Append one string onto the end of another.

    </li>

    <li>

           <bold>StringCompare</bold> - Case <bold>insensative</bold> comparison between strings.

    </li>

    <li>

           <bold>StringInsert</bold> - Insert one string into another.

    </li>

    <li>

           <bold>StringLength</bold> - Returns the number of characters in a string.

    </li>

    <li>

           <bold>StringSearch</bold> - Search through the string for a matching string.

    </li>

    <li>

           <bold>StringSlice</bold> - Cut a portion of the string out and into a new string.

    </li>

</ul>

 

 

 

 

<p/><p><bold><big>String escape sequences</big></bold></p>

 

<p>

    If you wish to include quotes within a string, you'll need to

    use some "escape sequences":

</p>

 

<p align=center><table>

    <tr>

           <td width=33% align=center><bold>\"</bold></td>

           <td width=66%>

                  Places a quote in the string.

                  Example: <bold>"This is a \"quote\"."</bold> will be displayed as <bold>This is a "quote".</bold>

           </td>

    </tr>

    <tr>

           <td width=33% align=center><bold>\'</bold></td>

           <td width=66%>

                  Single quote.

           </td>

    </tr>

    <tr>

           <td width=33% align=center><bold>\n</bold></td>

           <td width=66%>

                  Newline. Places a line-break between characters.

           </td>

    </tr>

    <tr>

           <td width=33% align=center><bold>\r\n</bold></td>

           <td width=66%>

                  Alternate way of writing a newline.

           </td>

    </tr>

    <tr>

           <td width=33% align=center><bold>\t</bold></td>

           <td width=66%>

                  Tab separator.

           </td>

    </tr>

    <tr>

           <td width=33% align=center><bold>\\</bold></td>

           <td width=66%>

                  Backslash

           </td>

    </tr>

    <tr>

           <td width=33% align=center><bold>\</bold>ddd</td>

           <td width=66%>

                  Octal character, where d is a digit from 0..7.

           </td>

    </tr>

    <tr>

           <td width=33% align=center><bold>\x</bold>ddd</td>

           <td width=66%>

                  Hex character, where d is a digit from 0..f.

           </td>

    </tr>

</table></p>

 

 

 

<p/><p><bold><big>String reference counting</big></bold></p>

 

<p>

    When you create a string, in the background MIFL allocates some

    memory for the string. Associated with the memory is a "reference count"

    that lets MIFL know if the string is still remembered by

    any variables, parameters, properties, etc. If the MIFL

    script "forgets" about the string then the memory is automatically

    deleted. This ensures that you won't get memory leaks from stings

    that are no longer in use.

</p>

 

<p>

    It also causes some noteworthy behavior. Look at the following

    code:

</p>

 

 

<p align=center><table><tr><td><font face=courier>

    var x = "Hello world!";<br/>

    var y = x;<br/>

    x.StringAppend ("more text");<br/>

    return y;

</font></td></tr></table></p>

 

<p>

    This function will return a string, "Hello world!more text".

    Here's why:

</p>

 

<ol>

    <li>

           "var x = "Hello world!"" creates a string and then stores

           a <bold>reference</bold> to it in x.

    </li>

    <li>

           "var y = x" does <bold>not</bold> copy the string, but copies

           the reference to the string. Therefore, x and y <bold>refer

           to the same string.</bold>

    </li>

    <li>

           "x.StringAppend("more text")" appends "more text" onto the

           string that <bold>both</bold> x and y are referencing.

    </li>

    <li>

           "return y" just returns another reference.

    </li>

</ol>

 

<p>

    This can get you if you're not careful because you may assume

    that "y = x" will copy the string, but it only copies the

    reference. Then, if you modify x you'll find that y is also

    modified, or vice versa.

    (Conversely, this can be a very useful feature.)

</p>

 

<p>

    If you want y to contain a copy of the string (so that modifying

    it won't affect x) then use:

</p>

 

<p align=center><table><tr><td><font face=courier>

    y = x.Clone();

</font></td></tr></table></p>

 

 

<p>

    This duplicates the string referred to by variable x,

    and sets y to the refer to the new string.

</p>

 

<p>

    Note: <bold>Concatenation with the + operator</bold> creates a

    copy of the strings, which is different behavior than StringAppend(),

    which modifies a string in place.

</p>

0.4  Lists

Tutorial about how to use lists in MIFL.

 

<p><bold><big>Lists</big></bold></p>

 

<p>

    A "list" is a data type that stores zero or more values in

    sequence. A list can store any mix of values.

    Lists are similar to ActiveScript's arrays.

    C/C++ don't   even contain lists.

</p>

 

<p>

    To create a list in code, use the left bracket ([), followed

    by some comma separated values, and finished up with the

    right bracket (]). A list can contain other lists.

</p>

 

<p>

    Some examples of lists are:

</p>

 

<ul>

    <li>

           <bold>[1, 54, 43, 23]</bold> - A list containing 4 numbers.

    </li>

    <li>

           <bold>["Mike", "George", "Amy", "Chris"]</bold> - A list containing 4 names.

    </li>

    <li>

           <bold>[1, "hello", 43, true]</bold> - This list has a mix of data types,

           including numbers, strings, and booleans.

    </li>

    <li>

           <bold>[1, ["hello", 43], true]</bold> - This list contains another

           list as it's 2nd element.

    </li>

    <li>

           <bold>[]</bold> - An empty list, with no elements.

    </li>

</ul>

 

 

 

 

 

 

<p/><p><bold><big>List operators</big></bold></p>

 

<p>

    Many of the operators work with lists:

</p>

 

<ul>

    <li>

           <bold>x = [53,324] + 435</bold> - This fills x

           with <bold>[53, 324, 435]</bold>, appending data onto the

           list.

    </li>

    <li>

           <bold>x = [53,324] + [435, 89]</bold> - This fills x

           with <bold>[53, 324, 435, 89]</bold>, appending the elements

           from the second list onto the first. If you wanted to

           generate [53, 324, [435, 89]] you'd need to call

           [53, 324].ListConcat ([435, 89]).

    </li>

    <li>

           <bold>x += [54,34]</bold> - This is interpreted as "x = x + [54,34]".

    </li>

    <li>

           <bold>[1,2] &lt; [2,2]</bold> - The &lt;, &lt;=,

           &gt; &gt;=, ==, and === operators do element-by-element comparisons

           of the two lists. Any string comparisons are case <bold>sensative</bold>.

           You need to use ListCompare() to do a case

           insensative compare.

    </li>

    <li>

           <bold>x = [54,34]; y = x[1];</bold> - Fills y in with the 2rd element (0 being the

           first element). y = 34.

    </li>

    <li>

           <bold>x = [54,34]; x[1] = "hello";</bold> - This fills x with [54,34],

           but then changes the 2rd element to "hello", resulting

           in [54, "hello"]. If the index is beyond the end of the list, the

           list will be extended with <bold>undefined</bold> values.

    </li>

</ul>

 

 

 

<p/><p><bold><big>List "methods"</big></bold></p>

 

<p>

    Lists support "methods", which are functions that automatically

    modify the list that they appear after. Example: To use the

    method "ListAppend" with the list in variable x,

    just use "x.ListAppend()".

</p>

 

<p>

    Below is a partial list of methods supported by lists.

    A complete list can be found under the "Reference" section of

    help, in "Data types", "Lists".

</p>

 

<ul>

    <li>

           <bold>ListAppend</bold> - Append a list or other data type onto the end of the list.

    </li>

    <li>

           <bold>ListCompare</bold> - Case <bold>insensative</bold> comparison between lists.

    </li>

    <li>

           <bold>ListInsert</bold> - Insert one list into another.

    </li>

    <li>

           <bold>ListNumber</bold> - Returns the number of elements in the list.

    </li>

    <li>

           <bold>ListSort</bold> - Sorts a list alphabetically or numerically.

    </li>

    <li>

           <bold>ListSlice</bold> - Cut a portion of the list out and into a new list.

    </li>

</ul>

 

 

 

 

 

<p/><p><bold><big>List reference counting</big></bold></p>

 

<p>

    When you create a list, in the background MIFL allocates some

    memory for the list. Associated with the memory is a "reference count"

    that lets MIFL know if the list is still remembered by

    any variables, parameters, properties, etc. If the MIFL

    script "forgets" about the list then the memory is automatically

    deleted. This ensures that you won't get memory leaks from lists

    that are no longer in use.

</p>

 

<p>

    It also causes some noteworthy behavior. Look at the following

    code:

</p>

 

 

<p align=center><table><tr><td><font face=courier>

    var x = [1,4,6];<br/>

    var y = x;<br/>

    x.ListAppend (99);<br/>

    return y;

</font></td></tr></table></p>

 

<p>

    This function will return a list, [1,4,6,99].

    Here's why:

</p>

 

<ol>

    <li>

           "var x = [1,4,6]" creates a list and then stores

           a <bold>reference</bold> to it in x.

    </li>

    <li>

           "var y = x" does <bold>not</bold> copy the list, but copies

           the reference to the list. Therefore, x and y <bold>refer

           to the same list.</bold>

    </li>

    <li>

           "x.ListAppend(99)" appends the element, 99, onto the

           list that <bold>both</bold> x and y are referencing.

    </li>

    <li>

           "return y" just returns another reference.

    </li>

</ol>

 

<p>

    This can get you if you're not careful because you may assume

    that "y = x" will copy the list, but it only copies the

    reference. Then, if you modify x you'll find that y is also

    modified, or vice versa.

    (Conversely, this can be a very useful feature.)

</p>

 

<p>

    If you want y to contain a copy of the list (so that modifying

    it won't affect x) then use:

</p>

 

<p align=center><table><tr><td><font face=courier>

    y = x.Clone();

</font></td></tr></table></p>

 

 

<p>

    This duplicates the list referred to by variable x,

    and sets y to the refer to the new list. It <bold>also</bold>

    duplicates all the data within the list, so any sublists or

    strings will also be cloned.

</p>

 

<p>

    Note: <bold>Concatenation with the + operator</bold> creates a

    copy of the list, which is different behavior than ListAppend(),

    which modifies a list in place.

</p>

0.5  Flow of Control

Describes flow-of-control commands.

 

<p>

    MIFL supports several different ways to control program flow,

    including if-then-else, while, do-while, for, and switch statements.

    I won't go into detail because the constructs are standard to

    all languages.

</p>

 

 

 

 

 

 

<p/><p><bold><big>Debug</big></bold></p>

 

<p>

    While technically not a flow-of-control statement, "Debug" fits

    best in this category. When a "Debug" statement is encountered,

    <bold>and</bold> the program is being run with debugging enabled,

    the debuggin user interface will be brought up so the user

    can step through the code.

</p>

 

<p>

    In C or C++ this is similar to placing an Assert() in the code

    and causing the development environment to bring up the debugger.

</p>

 

<p align=center><table><tr><td><font face=courier>

    <bold>Debug;</bold>

</font></td></tr></table></p>

 

 

<p>

    From within the for() loop you can use the

    following:

</p>

 

<ul>

    <li>

           <bold>Break;</bold> - Exits out of the statement.

    </li>

    <li>

           <bold>Continue;</bold> - Exits out of the statement, runs

           the increment expression, and

           returns to evaluating the conditional expression.

    </li>

</ul>

 

 

 

 

 

<p/><p><bold><big>For</big></bold></p>

 

<p>

    The for(;;) {}; loop works the same as C, C++, Java, and ActionScript:

</p>

 

<p align=center><table><tr><td><font face=courier>

    <bold>for (</bold><italic>init expression</italic><bold>,</bold><br/>

    &tab;<italic>conditional expression</italic><bold>,</bold><br/>

    &tab;<italic>increment expression</italic><bold>)</bold><br/>

    &tab;<italic>statement</italic><bold>;</bold><br/>

</font></td></tr></table></p>

 

 

<p>

    From within the for() loop you can use the

    following:

</p>

 

<ul>

    <li>

           <bold>Break;</bold> - Exits out of the statement.

    </li>

    <li>

           <bold>Continue;</bold> - Exits out of the statement, runs

           the increment expression, and

           returns to evaluating the conditional expression.

    </li>

</ul>

 

 

 

 

<p/><p><bold><big>If, then, else</big></bold></p>

 

<p>

    You can use the if-then-else statement to test conditionals.

    The format is the same as C, C++, Java, and ActionScript:

</p>

 

<p align=center><table><tr><td><font face=courier>

    <bold>if (</bold><italic>expression</italic><bold>)</bold><br/>

    &tab;<italic>statement</italic><bold>;</bold><br/>

</font></td></tr></table></p>

 

 

<p align=center><table><tr><td><font face=courier>

    <bold>if (</bold><italic>expression</italic><bold>)</bold><br/>

    &tab;<italic>if true statement</italic><bold>;</bold><br/>

    <bold>else</bold><br/>

    &tab;<italic>if false statement</italic><bold>;</bold>

</font></td></tr></table></p>

 

<p>

    You can also use the <bold>? :</bold> operator, as in

    other languages.

</p>

 

<p align=center><table><tr><td><font face=courier>

    <italic>expression</italic> <bold>?</bold><br/>

    &tab;<italic>if true statement</italic><br/>

    <bold>:</bold><br/>

    &tab;<italic>if false statement</italic><bold>;</bold>

</font></td></tr></table></p>

 

 

 

 

<p/><p><bold><big>Return</big></bold></p>

 

<p>

    Return works the same as C, C++, Java, and ActionScript.

    Calling return without an expression will

    return <bold>Undefined</bold>.

</p>

 

<p align=center><table><tr><td><font face=courier>

    <bold>return </bold><italic>expression</italic><bold>;</bold>

</font></td></tr></table></p>

 

<p align=center><table><tr><td><font face=courier>

    <bold>return;</bold>

</font></td></tr></table></p>

 

 

 

 

<p/><p><bold><big>Switch</big></bold></p>

 

<p>

    The switch(){}; statement is the same as C, C++, Java, and ActionScript:

    Unlike C and C++, the switch() statement can compare strings, objects,

    and any other data type, not just numeric values.

</p>

 

<p align=center><table><tr><td><font face=courier>

    <bold>switch (</bold><italic>expression</italic><bold>) {</bold><br/>

    <bold>case</bold> <italic>expression 1</italic><bold>:</bold><br/>

    &tab;<italic>statement</italic><bold>;</bold><br/>

    &tab;<bold>break;</bold><br/>

   

    <bold>case</bold> <italic>expression 2</italic><bold>:</bold><br/>

    &tab;<italic>statement</italic><bold>;</bold><br/>

    &tab;<bold>break;</bold><br/>

   

    <bold>default:</bold><br/>

    &tab;<italic>statement</italic><bold>;</bold><br/>

    &tab;<bold>break;</bold><br/>

    <bold>};</bold>

</font></td></tr></table></p>

 

 

<p>

    From within the switch() statement loops you can use the

    following:

</p>

 

<ul>

    <li>

           <bold>Break;</bold> - Exits out of the statement.

    </li>

</ul>

 

 

 

<p/><p><bold><big>While and do-while</big></bold></p>

 

<p>

    The while() {}; statement and do {} while (); statements

    allow for looping.

    The format is the same as C, C++, Java, and ActionScript:

</p>

 

<p align=center><table><tr><td><font face=courier>

    <bold>while (</bold><italic>expression</italic><bold>)</bold><br/>

    &tab;<italic>statement</italic><bold>;</bold><br/>

</font></td></tr></table></p>

 

 

<p align=center><table><tr><td><font face=courier>

    <bold>do</bold><br/>

    &tab;<italic>if true statement</italic><bold>;</bold><br/>

    <bold>while (</bold><italic>expression</italic><bold>)</bold>

</font></td></tr></table></p>

 

 

<p>

    From within the while() and do()-while loops you can use the

    following:

</p>

 

<ul>

    <li>

           <bold>Break;</bold> - Exits out of the statement.

    </li>

    <li>

           <bold>Continue;</bold> - Exits out of the statement and

           returns to evaluating the expression.

    </li>

</ul>

0.6  Objects – Basics

Tutorial on how to define and create objects.

 

<p><bold><big>MIFL objects vs. other languages</big></bold></p>

 

<p>

    MIFL is an object oriented language. It supports objects that are

    similar, but not exactly the same as those in C++, Java, and

    ActionScript.

</p>

 

 

<p>

    The basic premise of an object is a collection of related data

    (called properties)

    along with code (called methods) that are used to access or

    modify that data.

</p>

 

<p>

    For example: In an interactive fiction setting, two

    objects might be a "Parrot" and a "Penguin". Each would probably

    support the properties of "Weight" and "Height", but only

    the parrot object would have "FlyingSpeed" properties. They

    would both have "Walk" methods, but only the parrot would have

    a "Fly" method, and the penguin would have a "Swim" method.

</p>

 

<p>

    MIFL objects also include:

</p>

 

<ul>

    <li>

           <bold>Super-classes</bold> - The ability to "inherit" properties

           and methods from other objects. Both "Parrot" and "Penguin" might

           inheret some attributes and methods from the "Bird" super-classs

           (also an "object"). That might in turn inheret properties from

           the "Animal" super-class.

    </li>

    <li>

           <bold>Multiple inheritance</bold> - Objects can inherit properties

           and methods from more than one superclass. For example: The parrot

           might not only inherit from the "Bird" class, but from

           the "TalkingCreature" class. The penguin object might inherit

           from the "Bird" and "SwimmingCreature" class.

    </li>

    <li>

           <bold>Private methods and properties</bold> - Objects have

           methods and properties that can only be accessed from within

           the object. Usually, private methods and properties are used

           for "safety" reasons to ensure that foreign code doesn't

           call the private method or set a private variable that

           will "mess things up".

    </li>

    <li>

           <bold>Public methods and properties</bold> - All public methods

           and properties are globally defined. For example: Once the Walk() public

           method is defined, all objects that use the method Walk() must

           conform to the definition, including the number of parameters

           passed in. If they don't, compilation will fail. Conversely,

           C++ and Java allow a different Walk() to exist for each class.

    </li>

    <li>

           <bold>Dynamically created methods and properties (called "layers")</bold> - MIFL

           allows methods and properties to be added and removed dynamically

           to objects. C++ does not allow this.

    </li>

    <li>

           <bold>Timers</bold> - In MIFL, timers are associated with objects.

           C and C++ treat timers differently.

    </li>

    <li>

           <bold>Containership</bold> - In MIFL, an object can be contained

           by an object, and contain its own objects. Containership

           is very useful for interactive fiction. C, C++, Java, and

           ActionScript do not have containership.

    </li>

    <li>

           <bold>Blurring of the line between object and class</bold> - In

           C++, Java, and ActionScript, a class is a definition for an object,

           and an object is an instance of a class. To create an

           object the programmer must call "new object". MIFL also has this,

           but an class can automatically be created as an object and

           referenced in a global variable of the same name.

           This is useful for interactive fiction.

    </li>

    <li>

           <bold>Unique ID</bold> - Every MIFL object has an ID

           (128-bit GUID) that uniquely identifies it from all other objects

           created on this or other systems. The unique ID makes it easier

           to write objects to disk and then restore them. It also

           provides protection if a program access an object after it

           has been deleted.

    </li>

</ul>

 

 

 

 

 

<p/><p><bold><big>Creating an object/class</big></bold></p>

 

<p>

    Creating an object (or class of objects) is easy:

</p>

 

<ol>

    <li>

           Select the <bold>"Add new object"</bold> menu

           item in the <bold>"Objects"</bold> menu.

    </li>

    <li>

           In the "Modify object" page that appears,

           type in a <bold>"Name"</bold> for the object, such as "Parrot".

           It must be unique.

    </li>

    <li>

           You can also <bold>type in descriptions</bold> for the object and under

           what help categories it will be placed.

    </li>

</ol>

 

<p>

    The "Modify object" page has several tabs:

</p>

 

<ul>

    <li>

           <bold>Description</bold> - You just visited this.

    </li>

    <li>

           <bold>Super-classes</bold> - This lets you specify the

           super-classes of the object.

    </li>

    <li>

           <bold>Properties</bold> - The properties page lets you add

           or remove properties to the object.

    </li>

    <li>

           <bold>Methods</bold> - The methods page lets you add

           or remove methods to the object.

    </li>

    <li>

           <bold>Misc.</bold> - This page lets you move the object

           to a different library, and other settings.

    </li>

</ul>

 

 

 

<p/><p><bold><big>Making it an object</big></bold></p>

 

<p>

    MIFL makes it easy to automatically create an object from

    a class:

</p>

 

<ol>

    <li>

           Click on the <bold>"Super-classes"</bold> tab.

    </li>

    <li>

           Check <bold>"Automatically create as an object"</bold>.

    </li>

    <p>

           In future, if you want this object to be created within

           another object then select the other object from

           the drop-down immediately below the check-box.

           You could use this for interactive fiction to create objects

           that are contained with or held by other objects, such as

           a "Peanut" being held by the "Parrot" object.

           At the

           moment you can't do this because you only have one object.

    </p>

    <p>

           You can also change the ID object (underneath the super-class).

           In general, you only need to do this if you want to make sure

           that an object whose definition you deleted and then recreated

           will still use the same ID. This doesn't happen too often.

    </p>

</ol>

 

<p>

    If you didn't check the "Automatically create as an object" button

    then your definition would be more of a class (in C++ and Java

    terms).

</p>

 

<p>

    To test that your object actually works:

</p>

 

<ol>

    <li>

           <bold>"Compile"</bold> your project.

    </li>

    <li>

           Switch to the <bold>"Test compiled code"</bold> page.

    </li>

    <li>

           Click on the <bold>"Objects..."</bold> link on the right

           side of the page.

    </li>

    <p>

           This will expand to list all the objects (not classes).

    </p>

    <li>

           Click on the <bold>"Parrot"</bold> object.

    </li>

    <p>

           Underneath the "Parrot" object will be sub-tables

           for properties, methods, and timers supported by the

           object. (Right now they should be empty.) You can also hover

           your mouse over the "Contains" and "Layers" entries to

           see what objects your object contains (or is contained by),

           and what layers it has. Right now there won't be anything

           in the contains list, and only one layer, "Parrot".

    </p>

</ol>

 

 

 

 

 

<p/><p><bold><big>Adding properties</big></bold></p>

 

<p>

    To add public properties to your object, you must first

    define a public property. To do this:

</p>

 

<ol>

    <li>

           Select the <bold>"Add new property"</bold> menu

           item under <bold>"Property defs"</bold>.

    </li>

    <li>

           In the "Property definition" page, type in

           a <bold>"Name"</bold> for your property, such

           as <bold>"FlyingSpeed"</bold>.

    </li>

    <p>

           <bold>Note:</bold> If you leave the property

           value <bold>blank</bold> then the property

           will <bold>not</bold> be added to the object. However,

           if the property is inhereted from a superclass (discussed

           later) then that property will be used.

    </p>

    <li>

           You can also type in <bold>descriptions</bold> and help

           categories so that other authors will know what the

           "FlyingSpeed" property means.

    </li>

</ol>

 

<p>

    Now that you have created the public property definition,

    you can add the public property to your Parrot object:

</p>

<ol>

    <li>

           Select the <bold>"Parrot"</bold> menu item

           from the <bold>"Objects"</bold> menu.

    </li>

    <li>

           Click on the <bold>"Properties"</bold> tab.

    </li>

    <li>

           Under the "Property name" column, select

           the <bold>"FlyingSpeed"</bold> property from the drop-down

           list box.

    </li>

    <p>

           Once you have selected the property the description will

           automatically be filled in. You cannot modify the description

           of a public property from the properties page.

    </p>

    <li>

           To have the property automatically default to a value, type

           one in under <bold>"Initial value"</bold>. In C++ you would set the value

           in the object's constructor. You can do the same under MIFL,

           but setting it in the GUI is easier and better when you're

           trying to save the object to disk (since MIFL then knows if

           the property has changed from its initial value; MIFL doesn't

           save the property unless it has changed).

    </li>

    <li>

           You can test the property out by doing a <bold>Compile</bold> and

           then running <bold>Test compiled code</bold>. You can either

           see the property in the right-hand column, or

           bold type <bold>Parrot.FlyingSpeed</bold> in the watch list.

    </li>

    <p>

           Tip: If you change the <bold>"Scope"</bold> of the watch list to

           "Parrot" then you only need to type in "FlyingSpeed".

    </p>

</ol>

 

<p>

    You can add a private property in a similar manner:

</p>

 

<ol>

    <li>

           Select <bold>"Parrot"</bold> from the <bold>"Objects"</bold> menu.

    </li>

    <li>

           Switch to the <bold>"Properties"</bold> tab.

    </li>

    <li>

           Under the "Properties, private" section, type in

           a <bold>"Name"</bold> for your private property. (Remember,

           for a public property you selected from the public property

           list.)

    </li>

    <li>

           Set a <bold>"Initial value"</bold> just like the public property.

    </li>

    <li>

           Type in a <bold>"Description"</bold> of the private property.

           This doesn't appear in help, but it's always good to document

           what things mean.

    </li>

    <li>

           You can view the private property the same as a public

           property. Be aware that "Lantern.YourPrivateProperty"

           will <bold>not</bold> work unless you set the watch

           "Scope" to "Lantern". Private properties can only be accessed

           from within the object/class.

    </li>

</ol>

 

 

 

 

 

 

 

 

<p/><p><bold><big>Adding methods</big></bold></p>

 

<p>

    To add a public method to your object, you must first

    define a public method. To do this:

</p>

 

<ol>

    <li>

           Select the <bold>"Add new method"</bold> menu

           item under <bold>"Method defs"</bold>.

    </li>

    <li>

           In the "Modify method definition" page, type in

           a <bold>"Name"</bold> for your property, such

           as <bold>"Fly"</bold>.

    </li>

    <li>

           You can also type in <bold>descriptions</bold> and help

           categories so that other authors will know what the

           "Fly" method does.

    </li>

    <li>

           The "Method method definition" page is just like the

           "Modify function" page that you familiarized yourself

           with when you created HelloWorld(). The only difference

           is that it <bold>doesn't</bold> provide a tab for

           modifying the code, since a method definition does <bold>not</bold> include

           code; that's specific to an object.

    </li>

</ol>

 

<p>

    Now that you have created the public method definition,

    you can add the public method to your Parrot object:

</p>

 

<ol>

    <li>

           Select the <bold>"Parrot"</bold> menu item

           from the <bold>"Objects"</bold> menu.

    </li>

    <li>

           Click on the <bold>"Methods"</bold> tab.

    </li>

    <li>

           At the "Methods, public" section, select

           the <bold>"Fly"</bold> method from the drop-down list box.

    </li>

    <li>

           Press the <bold>"Add a new public method"</bold> button just

           above it. This will add the public method that you have selected

           from the list box.

    </li>

    <p>

           This switches to the "Modify method" page. Again it's

           just like the "Modify function" page, except that

           while you can modify the code, you cannot change the

           method's name, description, or parameters since these are

           set by the method definition.

    </p>

    <li>

           Switch to the <bold>"Code"</bold> tab and type in some

           code to run so you can see the method working:

    </li>

   

    <p align=center><table><tr><td><font face=courier>

           Trace ("Fly() method called");<br/>

           return FlyingSpeed;

    </font></td></tr></table></p>

 

    <li>

           You can test the property out by doing a <bold>Compile</bold> and

           then running <bold>Test compiled code</bold>.

    </li>

    <li>

           Type <bold>"Parrot.Fly()"</bold> in the edit field and

           press <bold>"Run this"</bold> to execute the code.

    </li>

    <p>

           You'll see the Trace() in the Debug Trace display, along

           with the value returned from Parrot.Fly(), which should be

           the value of FlyingSpeed.

    </p>

</ol>

 

<p>

    You can add a private property in a similar manner:

</p>

 

<ol>

    <li>

           Select <bold>"Parrot"</bold> from the <bold>"Objects"</bold> menu.

    </li>

    <li>

           Switch to the <bold>"Methods"</bold> tab.

    </li>

    <li>

           Under the "Methods, private" section, press

           the <bold>"Add a new private method"</bold> button.

    </li>

    <p>

           This switches to the "Modify method" page. Again it's

           just like the "Modify function" page. Unlike the public

           method modification, you can change everything, including

           the name, decription, parameters, and code.

    </p>

    <li>

           In the <bold>"Description"</bold> page type in

           a unique <bold>"Name"</bold> for the method.

    </li>

    <li>

           Switch to the <bold>"Code"</bold> tab and type in some

           code to run so you can see the method working.

    </li>

    <li>

           You can <bold>verify that the private method works</bold> the same

           way you tested the public method. However, you will

           need to set the watch variable's "Scope" to "Parrot"

           in order to access the private method.

    </li>

</ol>

 

 

 

 

 

<p/><p><bold><big>Dynamically creating and deleting objects</big></bold></p>

 

<p>

    You can also dynamically create and destroy objects:

</p>

 

<ol>

    <li>

           <bold>Compile</bold> the project and switch

           to the <bold>Test compiled code</bold> page.

    </li>

    <li>

           In the top edit field, type <bold>"MyGlobal = new Parrot"</bold> and

           press <bold>"Run this"</bold>.

    </li>

    <p>

           Note: This assumes that you kept your test global variable,

           "MyGlobal", in the code. If not, you'll need to add it.

    </p>

    <li>

           A second parrot will have been added. You can see it

           by clicking on <bold>"Objects..."</bold> in the right

           side of the "Test compiled code" page. The objects list will

           have two parrots, one named "Parrot" and another named

           "Parrot" with lots of numbers next to it.

    </li>

    <p>

           The parrot with all the numbers next to its name is the one

           you created. The numbers are the new parrot object's ID.

           Objects that are automatically created (such as the original

           Parrot) do not show this number.

    </p>

    <li>

           To delete the newly created parrot object, type <bold>"delete MyGlobal"</bold> and

           press <bold>"Run this"</bold>.

    </li>

    <p>

           Note: You're not deleting the global variable "MyGlobal", but

           the object referenced by the variable. There are ways to delete

           global variables, but they won't be covered in the tutorial.

    </p>

</ol>

 

0.7  Objects – Superclasses and subclasses

How to create superclasses and subclasses.

 

<p>

    Objects can inherit methods and properties from other classes/objects.

    As stated previously, this is useful for the example Parrot object

    that can inherit methods and properties from the soon-to-be-created

    "Bird" class, which in turn could inherit from an "Animal" class, etc.

</p>

 

<p>

    Each class provides a way of providing common methods and

    properties for a group of like individuals.

    A "superclass" is a class (or object) with methods or properties

    that are inherited by other classes. A "subclass" is a class

    that inherits from a superclass.

</p>

 

 

<p/><p><bold><big>Creating a superclass</big></bold></p>

 

 

<p>

    To create the "Bird" superclass:

</p>

 

<ol>

    <li>

           <bold>Create an object/class</bold> called "Bird", using the

           same general actions as those used to create "Parrot".

    </li>

    <li>

           Unlike Parrot, the <bold>"Automatically create as an object"</bold>,

           found under <bold>"Super-classes"</bold> in

           the "Modify object" page, should <bold>not</bold> be checked.

           (It could be checked, but then you'd end up with both a Parrot

           object and a Bird object.)

    </li>

    <li>

           As before, <bold>add the "FlightSpeed" public property</bold> to

           the Bird class, and type in an initial value, like 10.

    </li>

    <li>

           <bold>Add the "Fly" public method</bold> to the Bird class and provide

           that outputs a Trace("This is Fly from the bird class") so you

           can tell that it was called.

    </li>

</ol>

 

<p>

    That's it; you've created a bird class.

    (Of course, this is a very simple example so you can understand

    the process.)

</p>

 

 

 

<p/><p><bold><big>Using a superclass</big></bold></p>

 

<p>

    To get the Parrot object to use the Bird class:

</p>

 

<ol>

    <li>

           <bold>Switch to the</bold> "Modify object" page for the Parrot

           object, and press the <bold>"Super-classes"</bold> tab if

           it isn't already visible.

    </li>

    <li>

           Press the <bold>"Add class"</bold> button.

    </li>

    <p>

           The "Add class(es)" page will appear. It lists all of the

           classes in the project.

    </p>

    <li>

           <bold>Check</bold> the "Bird" class.

    </li>

    <li>

           <bold>Return</bold> to the "Modify object" page for the

           Parrot object. You'll see that "Bird" is now listed as

           one of the superclasses.

    </li>

    <p>

           If you compiled now though, the Parrot class would be unchanged

           because it already supports the "FlightSpeed" property and "Fly"

           method. Because of inheritance rules, if a sub-class (aka: the

           Parrot object) supports a method or property from a super-class (aka:

           the Bird class) then the sub-class's properties or methods

           have priority.

    </p>

    <p>

           Therefore, you need to remove both the "FlightSpeed" property

           and "Fly" method from the Parrot object (not the Bird class).

    </p>

    <li>

           Switch to the <bold>Properties</bold> tab and <bold>change</bold> the

           property drop-down for "FlightSpeed" to "blank", effectively

           deleting the property.

    </li>

    <li>

           Switch to the <bold>Methods</bold> tab, <bold>hold

           the control key down</bold>, and click on

           the <bold>"Fly"</bold> method button. This will delete the

           method.

    </li>

    <p>

           Since Parrot no longer supports the properties and methods

           from the Bird class, they will be inherited directly.

    </p>

    <li>

           To test your changes, <bold>compile</bold> and

           run the <bold>Test compiled code</bold>. The FlightSpeed

           property will be the value initialized in the Bird class,

           and the Fly method will call the code from the Bird class.

    </li>

</ol>

 

 

 

 

<p/><p><bold><big>Why use superclass?</big></bold></p>

 

 

<p>

    So what? That didn't really do much. Now that you have a

    Bird class that defined flying, here's how you can use it:

</p>

 

<ul>

    <li>

           <bold>Override a property's inheritance</bold> - If you add the

           "FlightSpeed" property back into the Parrot class but use

           a new initial value, you can make Parrots fly slower or

           faster than the "typical" bird.

    </li>

    <li>

           <bold>Override a method's inheritance</bold> - If you add the

           "Fly" method back into the Parrot class with different code

           you can make parrot's fly differentlu than "typical" birds.

    </li>

    <li>

           <bold>Other subclasses</bold> - Since most types of birds (swans,

           ducks, hawks, etc.) are really just small variations on the

           basic "bird" concept, you can quickly create many different

           types of birds (sub-classes) based on the "Bird" super-class.

           Where the type of bird (swan, duck, etc.) varies from the

           super-class "Bird", you provide new properties and/or methods.

    </li>

    <li>

           <bold>Multiple inheritance</bold> - As stated in the last

           tutorial, you can have the Parrot object inherit not only

           from the Bird class, but also from a "TalkingCreature" class.

           Parrots, humans, aliens, and even robots would be

           members of the "TalkingCreature" class, sharing properties

           and code specific to creature that talk.

    </li>

</ul>

 

 

<p>

    I won't lead you through all these examples step-by-step

    because they're all variations on a theme.

</p>

 

 

 

 

<p/><p><bold><big>Superclass order</big></bold></p>

 

<p>

    If an object is a sub-class to two or more super-classes

    you may need to prioritize the super-classes so that any

    overrides (for properties or methods) are correct. Such

    situations will arise if two or more superclasses support

    the same properties or methods.

</p>

 

<p>

    The <bold>"Super-classes"</bold> tab in the "Modify object"

    lets you control the priority of all the object's

    superclasses.

</p>

 

 

 

 

<p/><p><bold><big>Method inheritance</big></bold></p>

 

<p>

    Earlier I said that if a class (the Parrot object) provides

    code for a method (the "Fly" method),

    any code from the superclass (the "Fly" method in the Bird class)

    will be ignored.

</p>

 

<p>

    I lied. This isn't always the case.

</p>

 

<p>

    Some method definitions allow the methods to coexist.

    You can have a call

    to a method first call the method in the lowest sub-class (the

    Parrot), then the next sub-class (the Bird class), and the

    next one (the Animal class), etc. You can reverse the order

    and call the methods from the highest super-class first, down

    to the lowest sub-class. You can also allow this chain to

    be stopped based on the returns of the methods. This creates

    four possibilities, in addition to the "just override inheritance"

    that's the default.

</p>

 

<p>

    You might want to do this to accomplish:

</p>

 

<p>

    <bold>Cumulative effects</bold> - If you wanted to allow the

    bird with an egg inside it to fly slower, because it weighs more,

    you could temporarily decrease the "FlightSpeed" property and

    then try and remember to increase it when the egg was laid.

</p>

 

<p>

    Alternatively, you could create a "FlightSpeedGet" method that

    just did the following:

</p>

 

<p align=center><table><tr><td><font face=courier>

    return (parameter[0] = FlightSpeed);

</font></td></tr></table></p>

 

<p>

    This would return the bird's flying speed, and would be

    the "official" way of getting the value; you could even make

    the property private.

</p>

 

<p>

    The variable, parameter, is automatically defined to be the

    list of parameters passed into the method. Since the

    FlightSpeedGet() call takes no parameters, the list will

    initially be empty. Setting paramter[0], however, changes this.

    Normally, this would have no effect... however:

</p>

 

<p>

    You could also create a super-class called "Pregnant". It would

    support the FlightSpeedGet() method too, but its code would be:

</p>

 

<p align=center><table><tr><td><font face=courier>

    return (parameter[0] *= 0.75);

</font></td></tr></table></p>

 

<p>

    If you had the FlightSpeedGet() method call the super-class

    first, parameter[0] would be set to the original flight

    speed. Once that returned, the next class down (Pregnant) would

    be called; this would retrieve the value "stashed away" in

    parameter[0], decrease it to account for the egg's extra weight,

    and return that.

</p>

 

<p>

    Thus, to make a pregnant Parrot you'd include the Pregnant

    class as a super-class. You could also add and remove the

    pregnancy later using layers (discussed later).

</p>

 

<p>

    This idea could be extended further by making an "Unhealthy"

    super-class that likewise reduces flying speed. If a Parrot

    is both Pregnant and Unhealthy it will fly even slower.

</p>

 

 

<p>

    <bold>Sometimes capture, sometimes pass it on</bold> - Alternatively,

    you could tell the method definition to call all the methods

    from sub-to-super-class or super-to-sub-class, repeating until

    one of them comes up knows how to deal with the situation.

</p>

 

<p>

    Perhaps you might need a public method, LikeFood (FoodName), that returns

    true if the object (aka: Parrot or Bird) likes a food item (passed

    in as the parameter "FoodName") or false if it doesn't.

</p>

 

<p>

    The generic bird would return true for (FoodName == "birdseed") ||

    (FoodName == "peanut"), but

    false for (FoodName == "flower").

</p>

 

<p align=center><table><tr><td><font face=courier>

    return (FoodName == "birdseed") || (FoodName == "peanut");

</font></td></tr></table></p>

 

<p>

    Some parrots like flowers, in addition to to birdseed. You could

    provide have the LikeFood() method override, cut-and-paste the

    code from the Bird's LikeFood() method, and then modify it.

    Or, you could have specify (in the method definition) that it

    should call from the highest priority methods (the Parrot's)

    to the lowest priority methods (the Bird's), trying

    LikeFood() for each one. If any returns a value which is not

    undefined, then that return is used. Otherwise, the next in

    line is called.

</p>

 

<p>

    The Parrot's LikeFood() method would then be:

</p>

 

<p align=center><table><tr><td><font face=courier>

    if (FoodName == "flower")<br/>

    &tab;return true;<br/>

    else<br/>

    &tab;return undefined; // pass it on

</font></td></tr></table></p>

 

<p>

    This makes for a very elegant solution, especially if new

    foods are added later on.

</p>

 

 

<p>

    Both the "cumulative effects" and "sometimes capture, sometimes

    pass on" approaches to methods are supported by

    MIFL. To have a method definition use either of these:

</p>

 

<ol>

    <li>

           Switch to the <bold>"Misc."</bold> tab in the "Modify

           method definition" page for the method you wish to

           change.

    </li>

    <li>

           Change the <bold>"Overrides"</bold> setting to the method

           you wish to use. The default is "Call only the highest

           priority method", but the other four possibilities are

           also listed.

    </li>

    <li>

           Make sure to <bold>document</bold> this behavior so that

           anyone using that method definition will know what to expect.

    </li>

</ol>

 

 

 

 

 

 

<p/><p><bold><big>Recommended properties and methods</big></bold></p>

 

<p>

    Realistically, the flying speed for every bird is different,

    and while a few birds will fly at the FlightSpeed (property)

    as initialized by the Bird superclass, most birds will fly

    at different speeds.

</p>

 

<p>

    To make sure that anyone creating a new type of bird will

    override the default FlightSpeed property for the bird, or

    consciously decide not to override it, you can can add

    a setting to the "Bird" class so that if any other class

    is derived from it (such as a "Robin"), an entry for

    the FlightSpeed property will automaically be added to the

    Robin class's list of public properties.

</p>

 

<p>

    To do this:

</p>

 

<ol>

    <li>

           Switch to the <bold>"Misc."</bold> tab in the "Modify object"

           page for the "Bird" class.

    </li>

    <li>

           Press the <bold>"Recommend properties and methods for

           sub-classes"</bold> button.

    </li>

    <p>

           The "Recommended" page will appear, displaying the properties

           and methods that are automatically added to any sub-classes

           created from the Bird class.

    </p>

    <li>

           <bold>Check</bold> the "FlightSpeed" property.

    </li>

</ol>

 

<p>

    Once the "FlightSpeed" property is checked, anytime the Bird

    class is included as a super-class of another object (or class),

    the "FlightSpeed" property will automatically be added to

    the object's list of public properties.

</p>

0.8  Objects – Layers

Describes what layers are and how to use them.

 

<p>

    MIFL includes an object concept called <bold>"layers"</bold>.

    They allow an program to add new super-classes to an object

    at run-time. They also allow new methods and property get/set

    calls to be added at run-time.

</p>

 

<p>

    The primary use for layers is to allow objects to dynamically

    change their beahaviors (methods) and properties in an

    easy-to-program way. As an example from the last tutorial,

    layers could be used to add a "Pregnant" or "Unhealthy" superclass

    to a "Parrot" object. The layer allows all the methods

    associated with being Pregnant or Unhealthy to be added

    at run-time, and removed when the state no longer applied.

</p>

 

<p>

    To see how this works, first create a superclass that can be layered.

</p>

 

<ol>

    <li>

           <bold>Create</bold> an "Unhealthy" super-class.

    </li>

    <li>

           <bold>Add the public method,</bold> "Fly", to the Unhealthy super-class.

           The code should be different than before:

    </li>

</ol>

<p align=center><table><tr><td><font face=courier>

    Trace ("An unhealthy bird can't fly.");<br/>

    return false;

</font></td></tr></table></p>

 

<p>

    That's all you need to test out the concept. You'll know

    if the unhealthy superclass has been added to the Parrot

    object because calling Fly() will output different text.

</p>

 

<p>

    To test the Unhealthy superclass:

</p>

 

<ol>

    <li>

           <bold>Compile</bold> the project and

           then run the <bold>Test compile code</bold> menu item.

    </li>

    <li>

           Type <bold>"Parrot.Fly()"</bold> and press <bold>"Run this"</bold>.

    </li>

    <p>

           From the debug trace you'll see that the Fly() code for the Parrot

           comes from the Bird class. The parrot is still healthy.

    </p>

    <li>

           To add the Unhealthy class to the parrot object,

           type <bold>"Parrot.LayerAdd ("SickLayer", "Unhealthy", 1);"</bold> and

           press <bold>"Run this"</bold>.

    </li>

    <p>

           The "SickLayer" parameter is a name that you given to the layer

           so you can later idenfity which layer it is. (After all, you might

           have several layers.) The "Unhealthy" parameter is the name of the

           class. And 1 is the priority of the class; higher numbers have

           higher priority over other classes. The class used to create

           an object always has priorty 0, and is usually the lowest

           priority.

    </p>

    <li>

           Now, type <bold>"Parrot.Fly()"</bold> and press <bold>"Run this"</bold>.

    </li>

    <p>

           The Fly() code from the Unhealthy class will be run instead.

           The debug trace will indicate that the parrot is too sick to fly.

    </p>

    <li>

           You can also see the layer by using the right column

           of the test display. Click on <bold>"Objects..."</bold>,

           then <bold>"Parrot"</bold>, and finally, hover the mouse

           over the <bold>"Layers"</bold> entry.

    </li>

    <li>

           To remove the layer,

           type <bold>"Parrot.LayerRemove (0);"</bold> and

           press <bold>"Run this"</bold>.

    </li>

    <p>

           The 0 parameter is the index into the layer. If you used 1

           instead, the "Parrot" class would be removed from the object,

           making it an unhealthy nothing. Under normal circumstances

           you'd look through all the layers in an object to find

           the one named "SickLayer" and then delete that one. This

           is just a tutorial though.

    </p>

</ol>

 

<p>

    There are quite a few layer methods you can call. Some

    of them are:

</p>

 

<ul>

    <li>

           <bold>LayerAdd</bold> - Adds a new layer to an object.

    </li>

    <li>

           <bold>LayerGet</bold> - Gets information about a layer

    </li>

    <li>

           <bold>LayerNumber</bold> - Returns the number of layers in an object.

    </li>

    <li>

           <bold>LayerRemove</bold> - Removes a layer from an object.

    </li>

</ul>

 

 

 

<p/><p><bold><big>Adding individual methods</big></bold></p>

 

<p>

    You can also add individual methods to a layer (although

    adding classes (aka: groups of methods) is recommended).

    To add an individual method:

</p>

 

<ol>

    <li>

           In the "Test compiled code" page,

           type <bold>"Parrot.LayerMethodAdd (0, "Speak", Trace)"</bold> and

           then press <bold>"Run this"</bold>.

    </li>

    <p>

           The 0 parameter causes the method to be added to the layer

           at index location 0. "Speak" is the name that the new method

           will use. And, Trace, is the function (or method) to call when

           "Speak" is called.

    </p>

    <p>

           At the moment, you cannot compile code on-the-fly, so the

           only code you can add is that which has already been compiled,

           either in a function, this object, or another. (I can add

           the ability to code on-the-fly, if necessary.)

    </p>

    <li>

           Because "Speak" is not a compiled token, you can't

           just type "Parrot.Speak ()". However, you can call

           the method using, <bold>"Parrot.MethodCall ("Speak", "Polly wants

           a cracker."</bold> and pressing <bold>"Run this"</bold>.

    </li>

    <p>

           The "Speak" parameter is the name of the method to call; it

           could also be "Fly", or other methods supported by the Parrot.

           "Polly wants a cracker." is the parameter passed into the "Speak"

           method, which just ends up calling Trace().

    </p>

    <p>

           Therefore, you should see "Polly wants a cracker." appear

           on your debug trace.

    </p>

</ol>

 

 

 

<p/><p><bold><big>Layers and properties</big></bold></p>

 

<p>

    When you include a super-class in your object, it automatically

    inherits alls the properties from the superclass.

    This is <bold>not</bold> the case for layers. If you add

    a layer with a new class, the method definitions are incorporated

    into the object, but <bold>properties are not.</bold>

</p>

 

<p>

    MIFL specifically does not add properties when a layer is added

    since doing so might unpredictably (for the programmer) cause

    hundreds of properties to suddenly change.

</p>

 

<p>

    Of course, there's an easy work around for this: Just set the

    properties immediately before or after the layer. Or, provide

    a function in the layered class that initialzes any necessary

    properties.

</p>

 

<p>

    One aspect of the properties is inherited when you layer on

    a super-class though. Properties can include code that is called

    whenever the property is accessed, through a get() or set().

    Some properties need to trap access calls so they can verify the

    value they're being changed to is acceptable.

</p>

 

<p>

    If a layered super-class includes code for get() and set() of

    its properties, this get() and set() code will be incorporated

    into the object while the layer exists.

</p>

 

<p>

    Furthermore, just as there's a way to add individual methods

    to a layer, there is also a way of adding get() and set() code

    for properties to a layer. This won't be discussed in the

    tutorial though.

</p>

 

0.9  Objects – Containership

Tutorial about how one object can contain another.

 

<p>

    MIFL objects understand containership. They can contain other

    objects and be contained by an object. This is an extremely

    useful tool for interactive fiction, which has all sorts

    of containers, like rooms containing objects, characters

    containing inventory objects, and backpacks containing

    objects.

</p>

 

<p>

    To have an object initially created within another object:

</p>

 

<ol>

    <li>

           First create an object that can be a container. Using

           everything you've learned so far, <bold>create

           a "Birdcage"</bold> object. It doesn't need any properties

           or methods. Make sure it has the <bold>"Automatically

           create as an object"</bold> option checked.

    </li>

    <li>

           Modify the Parrot object so it starts out contained within

           the birdcage. Bring up the Parrot's "Modify object" page

           and switch to the <bold>"Super-classes"</bold> tab.

    </li>

    <li>

           Underneath the "Automatically create as an object" checkbox

           is a drop-down listbox. Open it up and select <bold>"Birdcage"</bold> from

           the listbox.

    </li>

    <li>

           <bold>Compile</bold> the code and go to <bold>Test

           compiled code</bold>.

    </li>

    <li>

           You can use the right column to see the containership;

           click on <bold>"Objects..."</bold>, then <bold>"Birdcage"</bold>,

           and hover the mouse over the <bold>"Contains"</bold> item.

           A popup will appear showing that the birdcage contains

           the parrot.

    </li>

    <li>

           If you <bold>switch to</bold> the Parrot object you'll see

           that it is contained by the birdcage.

    </li>

</ol>

 

<p>

    That's all you need to do.

</p>

 

<p>

    You can change containership at run-time using the following

    methods:

</p>

 

<ul>

    <li>

           <bold>ContainedInGet</bold> - Returns the object that contains

           this object.

    </li>

    <li>

           <bold>ContainedInSet</bold> - Moves the object into another

           object.

    </li>

    <li>

           <bold>ContainsEnum</bold> - Returns a list of objects

           that this object contains.

    </li>

</ul>

0.10       Objects – Timers

Tutorial about the use of timers.

 

<p>

    In MIFL, all timer events are associated with objects.

    This means that to enumerate all the timers, you need to enumerate each

    object and discover what times it supports. It also

    causes times to be deleted when the object is deleted, or

    saved when the object is saved.

</p>

 

<p>

    Creating a timer is easy:

</p>

 

<ol>

    <li>

           Switch to the <bold>Test compiled code</bold> window.

    </li>

    <li>

           Type <bold>"Parrot.TimerAdd ("SpeakTimer", true,

           1.0, Trace, "Polly wants a cracker.")</bold> and

           press <bold>"Run this"</bold>.

    </li>

    <p>

           The "SpeakTimer" is a name you can use to identify the

           timer. True indicates that the timer keeps repeating; if you

           passed in false it would only call itself once and then stop.

           1.0 is the number of seconds before the timer goes off.

           Trace is the function (or method) to call, and

           "Polly wants a cracker." is the parameter to pass into the

           function (or method).

    </p>

    <p>

           In other words, this timer causes the parrot to speak,

           "Polly wants a cracker." over and over again.

    </p>

    <li>

           To see the effects, press the <bold>"Refresh"</bold> button

           in the "Debug trace" section.

    </li>

    <li>

           You can also see the timer by clicking, <bold>"Timers (active)..."</bold> in

           the right-hand table.

    </li>

</ol>

 

 

<p>

    There are three ways to stop the timer:

</p>

 

<ul>

    <li>

           Run <bold>"Parrot.TimerRemove ("SpeakTimer")"</bold>. This

           deletes the timer.

    </li>

    <li>

           Run <bold>"Parrot.TimerSuspendedSet (0.0)"</bold>. This

           suspends <bold>all</bold> of the timers in the parrot object.

           You can reactivate the timers by calling this function and

           passing false in as the parameter.

    </li>

    <li>

           Delete the parrot by running <bold>"delete Parrot"</bold>.

    </li>

</ul>

 

<p>

    That's the basics. Here's a complete list of timer methods:

</p>

 

<ul>

    <li>

           <bold>TimerAdd</bold> - Adds a new timer to the object.

    </li>

    <li>

           <bold>TimerEnum</bold> - Enumerates all the timers in an object.

    </li>

    <li>

           <bold>TimerQuery</bold> - Returns information about a timer.

    </li>

    <li>

           <bold>TimerRemove</bold> - Deletes a timer from an object.

    </li>

    <li>

           <bold>TimerSuspendedGet</bold> - Returns TRUE if the object's

           timers are suspended.

    </li>

    <li>

           <bold>TimerSuspendedSet</bold> - Suspends or resumes an

           object's timers.

    </li>

</ul>

 

0.11       String tables and resources

Tutorial about the use of timers.

 

<p>

    MIFL includes two special entities, string tables and resources.

    The are both extremely useful for writing an application.

</p>

 

 

 

<p/><p><bold><big>String table</big></bold></p>

 

<p>

    The <bold>"String table"</bold> is a list of strings with

    a name attached to each. For example: One entry in the string

    table might be "Hello world!" with the name, SHelloWorld (the S

    prefix indicating it's a string).

</p>

 

<p>

    When you have entered a string into the string table, you

    can refer to the string by it's name. Therefore,

    calling Trace(SHelloWorld); will print out "Hello world!".

</p>

 

<p>

    The advantages of this follow:

</p>

 

<ul>

    <li>

           <bold>Centralized</bold> - If someone needs to change a string

           that's displayed to the user, they can look for it in one (centralized)

           location, instead of scanning through all the code.

    </li>

    <li>

           <bold>Multiple references</bold> - If a string is used over and over

           again, you only need to keep one copy of it in the string table.

           This not only saves memory, but it makes it easy to change

           all occurances of the string at once.

    </li>

    <li>

           <bold>Faster code</bold> - A reference to a string table (SHelloWorld)

           is stored internally as a number, making it very fast to pass

           around. The string table reference is only converted to

           a string, "Hello world!", at the last moment.

    </li>

    <li>

           <bold>Localization</bold> - This is the most important aspect of

           the string table. If a program only references SHelloWorld then

           translating the program to spanish only requires that

           the string table for SHelloWorld be changed from "Hello world!"

           to "Hola el mundo!".

    </li>

    <p>

           Here's the really cool part... the string table can contain the

           string for both "Hello world!" and "Hola el mundo!". The version

           it uses depends upon the user's language. This allows your application

           to be written with English, Spanish, Japanese, etc. strings already

           translated. If someone in an English speaking country runs it,

           your program will use English. If they run it from Japan (or otherwise

           indicate they're Japanese), they get Japanese from the program.

    </p>

    <p>

           Furthermore, if MIFL is used for an online interactive fiction server, then

           users in the US will see English prompts, and users from

           Japan will see Japanese prompts, even though it's the same

           program running. (Their chat text won't be translated though.)

    </p>

</ul>

 

 

<p>

    Before creating a string (in the string table), you should

    first identify what languages your project will be

    translated into:

</p>

 

<ol>

    <li>

           Select the <bold>"Project settings"</bold> menu item

           under the <bold>"Misc.</bold> menu.

    </li>

    <p>

           In the "Project settings" page that appears, you'll find

           a section for "Languages". This lists all the languages

           supported by your project.

    </p>

    <li>

           To add a new language, <bold>select</bold> the language

           underneath "Add language" and then

           press <bold>"Add language"</bold>. You will see the

           language added in the list to the right.

    </li>

</ol>

 

 

<p>

    To add a string to the string table, just:

</p>

 

<ol>

    <li>

           Select the <bold>"Add new string"</bold> menu item

           under the <bold>"Misc.</bold> menu.

    </li>

    <p>

           The "Modify string" page will appear.

    </p>

    <li>

           Type in the <bold>"Name"</bold>, such as "SHelloWorld".

    </li>

    <li>

           <bold>Type in the string</bold> for each of the languages.

           You might end up leaving some languages blank because you

           don't know them and are relying on a translator.

    </li>

    <p>

           If a language's string is blank when the program is run,

           the first non-blank string will be used, even though it

           isn't for the right language.

    </p>

    <li>

           Revisit any code you wrote and <bold>replace</bold> all occurances

           of "Hello world!" with SHelloWorld.

    </li>

    <li>

           <bold>Compile</bold> your code and use the <bold>Test

           compiled code</bold> page to test that the strings still

           display.

    </li>

</ol>

 

<p>

    Even if you added Japanese translation of "Hello world!", when

    your HelloWorld() function was called, it probably printed

    out "Hello world!". This is because it thinks you're in

    an English speaking area. (Although theoretically if you're

    in Japan it will automatically default to Japanese, and instead,

    refuse to display the English version.)

</p>

 

<p>

    To tell MIFL what language to use:

</p>

 

<ol>

    <li>

           In the "Test compiled code" page,

           type <bold>"LanguageSet(1041)"</bold> and

           press <bold>"Run this"</bold>.

    </li>

    <p>

           The 1041 parameter is an ID for Japanese. English is 1033.

           The language IDs conform to the standard LANGID #defines in

           winnt.h. If you don't have this but wish to use the translations,

           please contact me.

    </p>

    <li>

           Calling <bold>HelloWorld()</bold> will now say "Hello world!" in

           Japanese, assuming that you had entered a Japanese translation

           for the string.

    </li>

    <li>

           To find out what language MIFL is using

           type <bold>"LanguageGet()"</bold> and

           press <bold>"Run this"</bold>. The return value is the

           current language ID.

    </li>

</ol>

 

 

<p>

    <bold>Warning:</bold> - Using a string table with multiple

    languages can introduce some difficult-to-find bugs. For

    example, look at this code:

</p>

 

<p align=center><table><tr><td><font face=courier>

    MyGlobal = SHelloWorld;<br/>

    Trace (MyGlobal);

</font></td></tr></table></p>

 

<p>

    This will write "Hello world!" to the debug trace if the

    language is set to English, "Hola el mundo!" for Spanish, etc.

    No problems here. Lets complicate matters:

</p>

 

 

<p align=center><table><tr><td><font face=courier>

    MyGlobal = SHelloWorld + " " + SHelloWorld;<br/>

    Trace (MyGlobal);

</font></td></tr></table></p>

 

<p>

    This will write "Hello world! Hello world!" to the debug trace if the

    language is set to English, "Hola el mundo! Hola el mundo!" for Spanish, etc.

    No problems here. Now for the problem:

</p>

 

<p align=center><table><tr><td><font face=courier>

    LanguageSet (1033);<br/>

    MyGlobal = SHelloWorld + " " + SHelloWorld;<br/>

    LanguageSet (1034);<br/>

    Compare = SHelloWorld + " " + SHelloWorld;<br/>

    Trace (MyGlobal == Compare);

</font></td></tr></table></p>

 

<p>

    The trace output is <bold>false</bold>. One might expect

    it to be true since the exact same sequence of operations

    is used to produce MyGlobal as well as Compare. Why is this?

</p>

 

<p>

    "MyGlobal = SHelloWorld + " " + SHelloWorld;" ends up converting

    SHelloWorld into a string. Since the current language is English,

    the English version of SHelloWorld will be used from the string table.

    MyGlobal ends up with the string "Hello world! Hello world!".

</p>

 

<p>

    However, "LanguageSet (1034);" changes the language to

    Spanish. When "Compare = SHelloWorld + " " + SHelloWorld;" is called,

    the Spanish versions of the string are used.

    Compare ends up being "Hola el mundo! Hola el mundo!".

</p>

 

<p>

    The two strings are compared (as strings). To the program

    they're obviously not the same.

</p>

 

 

 

 

 

<p/><p><bold><big>Resources</big></bold></p>

 

<p>

    <bold>Resources</bold> are desgigned to hold data that isn't

    a string and isn't code. This could include images,

    sounds, etc.

</p>

 

<p>

    Unforuntately, the type of resources a MIFL

    project contains depends upon its context; different types

    of resources will be available for interactive fiction than

    for 3D modelling. Since this tutorial is written to be

    context independent, it won't be very specific about the

    types resources.

</p>

 

<p>

    To create a resource:

</p>

 

<ol>

    <li>

           Select the <bold>"Add new resource"</bold> menu item

           under the <bold>"Misc."</bold> menu.

    </li>

    <li>

           A new page, "Add a resource", will appear. <bold>Click on</bold> one

           of the listed resource types. Unfortunately, I can't

           be detailed here.

    </li>

    <p>

           When you click on a resource, a new page will appear, "Modify

           resource". It lets you change the name of the resource as well

           adding resources for specific languages.

    </p>

    <li>

           Type in a <bold>"Name"</bold>, such as "RMyResource". The

           "R" prefix indictes it's a resource.

    </li>

    <li>

           Type in a <bold>"Description"</bold> so that when you see

           the list of resources you'll know what it's for.

    </li>

    <li>

           Press <bold>"Add"</bold> for one of the languages. This

           will bring up a dialog that's specific to modifying the

           resource you have selected.

    </li>

    <p>

           If you have specified multiple languages for your project,

           you'll notice that the resource can contain slots for each

           of the languages. This lets the resource automatically

           adjust to the user's language, just as string tables did.

           If this were an audio resource with a recording of someone speaking,

           you could include several recordings, one for each

           supported language.

    </p>

    <p>

           And, just as with string tables, if you only fill in one

           resource entry, that one will be used, despite the user's

           choice of lanugages. You probably don't need localized versions

           of ever image, for example, although signs and other letters

           within the images might cause a problem.

    </p>

</ol>

 

<p>

    Resource usage is context dependent, so I can't show that

    here. As a general rule, the content (such as interactive fiction)

    will provide a library specific to the context. It will

    contain functions for using the resources the context supports.

</p>

Constructors and destructors

Describes how constructors and destructors work in MIFL.

 

<p>

    MIFL supprots constructors and destructors for objects.

    A constructor is called when an object is created, and

    a destructor is called when an object is deleted (usually).

    Classes and objects typically use constructors

    to initialize variables and start timers. Destructors are usually

    to unravel relationships the object has with other objects.

</p>

 

<p>

    The use of a constructor and/or destructor for an object

    is option. To use one, the object needs to support

    the public method <bold>Constructor()</bold> and/or

    the public method <bold>Destructor()</bold>.

    It may also want to support <bold>Constructor2()</bold>.

</p>

 

 

<p>

    Constructor() and Desctructor() are called under the following

    circumstances:

</p>

 

<ul>

    <li>

           When a program is first run, <bold>all of</bold> the automatically

           created objects are created. Then, <bold>Constructor()</bold> is

           called for all the objects.

    </li>

    <li>

           If a running program has been saved to disk, and is reloaded,

           the <bold>Constructor2()</bold> method will be called for all

           the loaded objects instead of Constructor().

    </li>

    <li>

           When an object is created using

           "new", <bold>Constructor()</bold> will be called for the object.

    </li>

    <li>

           When an object is deleted using

           "delete", <bold>Destructor()</bold> will be called for the object.

           The destructor will be called <bold>before</bold> all the timers

           have been deleted.

    </li>

    <li>

           If a program is shut down even though objects are still

           around, then Destructor() will <bold>not</bold> be called.

    </li>

</ul>

Datatype overview

Describes the different data types.

 

<p>

    MIFL supports the following data types:

</p>

 

 

<p/><p><bold><big>Boolean</big></bold></p>

 

<p>

    A boolean can either be <bold>true</bold> or <bold>false</bold>.

    Both true and false are keywords built into the language.

</p>

 

<p align=center><table bgcolor=#ffffff>

    <tr><td bgcolor=#80c0ff><bold>Convert data-type to boolean</bold></td></tr>

    <tr><td bgcolor=#80c0ff><bold>Data-type</bold></td><td bgcolor=#80c0ff><bold>Conversion</bold></td></tr>

    <tr>

           <td>Boolean</td>

           <td>NA</td>

    </tr>

    <tr>

           <td>Character</td>

           <td>if '\0' then false, else true</td>

    </tr>

    <tr>

           <td>Function</td>

           <td>true</td>

    </tr>

    <tr>

           <td>List</td>

           <td>true</td>

    </tr>

    <tr>

           <td>List.Method</td>

           <td>true</td>

    </tr>

    <tr>

           <td>Null</td>

           <td>false</td>

    </tr>

    <tr>

           <td>Number</td>

           <td>if 0 then false, else true</td>

    </tr>

    <tr>

           <td>Method</td>

           <td>true</td>

    </tr>

    <tr>

           <td>Object</td>

           <td>true</td>

    </tr>

    <tr>

           <td>Object.Method</td>

           <td>true</td>

    </tr>

    <tr>

           <td>Resource</td>

           <td>true</td>

    </tr>

    <tr>

           <td>String</td>

           <td>if "" then false, else true</td>

    </tr>

    <tr>

           <td>String.Method</td>

           <td>true</td>

    </tr>

    <tr>

           <td>String table</td>

           <td>if "" then false, else true</td>

    </tr>

    <tr>

           <td>Undefined</td>

           <td>false</td>

    </tr>

</table></p>

 

 

 

<p/><p><bold><big>Character</big></bold></p>

 

 

<p>

    A character is a unicode character, from 0 to 65535.

    Characters are specific by using single-quotes around a single

    character, such as <bold>'x'</bold> or <bold>'?'</bold>.

    The same escape sequences that strings use can appear in

    a character, such as <bold>'\n'</bold>.

</p>

 

<p align=center><table bgcolor=#ffffff>

    <tr><td bgcolor=#80c0ff><bold>Convert data-type to character</bold></td></tr>

    <tr><td bgcolor=#80c0ff><bold>Data-type</bold></td><td bgcolor=#80c0ff><bold>Conversion</bold></td></tr>

    <tr>

           <td>Boolean</td>

           <td>'t' if true, 'f' if false</td>

    </tr>

    <tr>

           <td>Character</td>

           <td>NA</td>

    </tr>

    <tr>

           <td>Function</td>

           <td></td>

    </tr>

    <tr>

           <td>List</td>

           <td>'\0'</td>

    </tr>

    <tr>

           <td>List.Method</td>

           <td>'\0'</td>

    </tr>

    <tr>

           <td>Null</td>

           <td>'\0'</td>

    </tr>

    <tr>

           <td>Number</td>

           <td>convert to Unicode character</td>

    </tr>

    <tr>

           <td>Method</td>

           <td>'\0'</td>

    </tr>

    <tr>

           <td>Object</td>

           <td>'\0'</td>

    </tr>

    <tr>

           <td>Object.Method</td>

           <td>'\0'</td>

    </tr>

    <tr>

           <td>Resource</td>

           <td>'\0'</td>

    </tr>

    <tr>

           <td>String</td>

           <td>first letter of the string</td>

    </tr>

    <tr>

           <td>String.Method</td>

           <td>'\0'</td>

    </tr>

    <tr>

           <td>String table</td>

           <td>first letter of the string</td>

    </tr>

    <tr>

           <td>Undefined</td>

           <td>'\0'</td>

    </tr>

</table></p>

 

 

 

 

 

<p/><p><bold><big>Function</big></bold></p>

 

<p>

    This is a reference to a function. Function names are

    tokens generated at compile time.

</p>

 

<p align=center><table bgcolor=#ffffff>

    <tr><td bgcolor=#80c0ff><bold>Convert data-type to function</bold></td></tr>

    <tr><td bgcolor=#80c0ff><bold>Data-type</bold></td><td bgcolor=#80c0ff><bold>Conversion</bold></td></tr>

    <tr>

           <td>Boolean</td>

           <td>undefined</td>

    </tr>

    <tr>

           <td>Character</td>

           <td>undefined</td>

    </tr>

    <tr>

           <td>Function</td>

           <td>NA</td>

    </tr>

    <tr>

           <td>List</td>

           <td>undefined</td>

    </tr>

    <tr>

           <td>List.Method</td>

           <td>undefined</td>

    </tr>

    <tr>

           <td>Null</td>

           <td>undefined</td>

    </tr>

    <tr>

           <td>Number</td>

           <td>undefined</td>

    </tr>

    <tr>

           <td>Method</td>

           <td>undefined</td>

    </tr>

    <tr>

           <td>Object</td>

           <td>undefined</td>

    </tr>

    <tr>

           <td>Object.Method</td>

           <td>undefined</td>

    </tr>

    <tr>

           <td>Resource</td>

           <td>undefined</td>

    </tr>

    <tr>

           <td>String</td>

           <td>searches through the function list for the name</td>

    </tr>

    <tr>

           <td>String.Method</td>

           <td>undefined</td>

    </tr>

    <tr>

           <td>String table</td>

           <td>searches through the function list for the name</td>

    </tr>

    <tr>

           <td>Undefined</td>

           <td>undefined</td>

    </tr>

</table></p>

 

 

 

 

 

 

 

<p/><p><bold><big>List</big></bold></p>

 

 

<p>

    A list is an array of values (which can be any of the datatypes,

    including other lists). For more information on lists, see

    the tutorial about them.

</p>

 

<p align=center><table bgcolor=#ffffff>

    <tr><td bgcolor=#80c0ff><bold>Convert data-type to list</bold></td></tr>

    <tr><td>No conversions possible.</td></tr>

</table></p>

 

 

 

 

 

<p/><p><bold><big>List.Method</big></bold></p>

 

<p>

    A "List.Method" is a combination of a list and a method assocaited

    with that list. Although this is a dataype, programmers won't

    usually think of it as one since it's an intermediate step

    towards accessing the list's methods, such as List.ListAdd();

</p>

 

<p align=center><table bgcolor=#ffffff>

    <tr><td bgcolor=#80c0ff><bold>Convert data-type to list</bold></td></tr>

    <tr><td>No conversions possible.</td></tr>

</table></p>

 

 

 

 

<p/><p><bold><big>Null</big></bold></p>

 

<p>

    The null type has no values.

</p>

 

<p align=center><table bgcolor=#ffffff>

    <tr><td bgcolor=#80c0ff><bold>Convert data-type to list</bold></td></tr>

    <tr><td>No conversions possible.</td></tr>

</table></p>

 

 

<p/><p><bold><big>Number</big></bold></p>

 

<p>

    A number is a floating point value (in C/C++ terms it's

    an 8-byte double) that has 15 digits of precision and ranges

    from (approx) -1e300 to 1e300.

</p>

 

<p>

    A number can be written in the following forms:

</p>

<ul>

    <li>

           <bold>Integer</bold> - A series of digits. A negative in front

           is optional.

    </li>

    <li>

           <bold>Decimal</bold> - An optional negative, with (optional) digits followed

           by a '.' and more digits.

    </li>

    <li>

           <bold>Exponential</bold> - A decimal number immediately followed by

           'e' and then an integer.

    </li>

    <li>

           <bold>Hexadecimal</bold> - '0x' followed by any number of hexadecimal

           digits ('0'..'9', 'a'..'f').

    </li>

</ul>

 

 

<p align=center><table bgcolor=#ffffff>

    <tr><td bgcolor=#80c0ff><bold>Convert data-type to number</bold></td></tr>

    <tr><td bgcolor=#80c0ff><bold>Data-type</bold></td><td bgcolor=#80c0ff><bold>Conversion</bold></td></tr>

    <tr>

           <td>Boolean</td>

           <td>0 if false, 1 if true</td>

    </tr>

    <tr>

           <td>Character</td>

           <td>Unciode value of the character</td>

    </tr>

    <tr>

           <td>Function</td>

           <td>0</td>

    </tr>

    <tr>

           <td>List</td>

           <td>0</td>

    </tr>

    <tr>

           <td>List.Method</td>

           <td>0</td>

    </tr>

    <tr>

           <td>Null</td>

           <td>0</td>

    </tr>

    <tr>

           <td>Number</td>

           <td>NA</td>

    </tr>

    <tr>

           <td>Method</td>

           <td>0</td>

    </tr>

    <tr>

           <td>Object</td>

           <td>0</td>

    </tr>

    <tr>

           <td>Object.Method</td>

           <td>0</td>

    </tr>

    <tr>

           <td>Resource</td>

           <td>0</td>

    </tr>

    <tr>

           <td>String</td>

           <td>convert the first characters of the string to a number</td>

    </tr>

    <tr>

           <td>String.Method</td>

           <td>0</td>

    </tr>

    <tr>

           <td>String table</td>

           <td>convert the first characters of the string to a number</td>

    </tr>

    <tr>

           <td>Undefined</td>

           <td>0</td>

    </tr>

</table></p>

 

 

 

 

 

 

 

<p/><p><bold><big>Method</big></bold></p>

 

 

<p>

    This is a reference to a method. Method names are

    tokens generated at compile time.

</p>

 

<p align=center><table bgcolor=#ffffff>

    <tr><td bgcolor=#80c0ff><bold>Convert data-type to method</bold></td></tr>

    <tr><td bgcolor=#80c0ff><bold>Data-type</bold></td><td bgcolor=#80c0ff><bold>Conversion</bold></td></tr>

    <tr>

           <td>Boolean</td>

           <td>undefined</td>

    </tr>

    <tr>

           <td>Character</td>

           <td>undefined</td>

    </tr>

    <tr>

           <td>Function</td>

           <td>undefined</td>

    </tr>

    <tr>

           <td>List</td>

           <td>undefined</td>

    </tr>

    <tr>

           <td>List.Method</td>

           <td>keep only the method</td>

    </tr>

    <tr>

           <td>Null</td>

           <td>undefined</td>

    </tr>

    <tr>

           <td>Number</td>

           <td>undefined</td>

    </tr>

    <tr>

           <td>Method</td>

           <td>NA</td>

    </tr>

    <tr>

           <td>Object</td>

           <td>undefined</td>

    </tr>

    <tr>

           <td>Object.Method</td>

           <td>keep only the method</td>

    </tr>

    <tr>

           <td>Resource</td>

           <td>undefined</td>

    </tr>

    <tr>

           <td>String</td>

           <td>searches through the public method list for the name</td>

    </tr>

    <tr>

           <td>String.Method</td>

           <td>keep only the method</td>

    </tr>

    <tr>

           <td>String table</td>

           <td>searches through the public method list for the name</td>

    </tr>

    <tr>

           <td>Undefined</td>

           <td>undefined</td>

    </tr>

</table></p>

 

 

 

 

<p/><p><bold><big>Object</big></bold></p>

 

 

 

<p>

    This is a reference to an object.

</p>

 

 

<p align=center><table bgcolor=#ffffff>

    <tr><td bgcolor=#80c0ff><bold>Convert data-type to list</bold></td></tr>

    <tr><td>No conversions possible.</td></tr>

</table></p>

 

 

 

 

 

 

<p/><p><bold><big>Object.Method</big></bold></p>

 

 

<p>

    An "ObjectMethod" is a combination of an object and a method assocaited

    with that list. Although this is a dataype, programmers won't

    usually think of it as one since it's an intermediate step

    towards accessing the object's methods, such as Object.MyMethod();

    It is a useful construct for timers and other callbacks since

    it remembers both the method to call and object to which it

    belongs.

</p>

 

<p align=center><table bgcolor=#ffffff>

    <tr><td bgcolor=#80c0ff><bold>Convert data-type to object.method</bold></td></tr>

    <tr><td>No conversions possible.</td></tr>

</table></p>

 

 

 

 

<p/><p><bold><big>Resource</big></bold></p>

 

 

<p>

    A resource is a reference to a resource (in the project).

    Resource names are generated at compile time.

</p>

 

<p align=center><table bgcolor=#ffffff>

    <tr><td bgcolor=#80c0ff><bold>Convert data-type to resource</bold></td></tr>

    <tr><td>No conversions possible.</td></tr>

</table></p>

 

 

 

<p/><p><bold><big>String</big></bold></p>

 

<p>

    A string is a reference to memory containing a Unicode string.

    For more information on strings see the tutorial.

</p>

 

 

<p align=center><table bgcolor=#ffffff>

    <tr><td bgcolor=#80c0ff><bold>Convert data-type to string</bold></td></tr>

    <tr><td bgcolor=#80c0ff><bold>Data-type</bold></td><td bgcolor=#80c0ff><bold>Conversion</bold></td></tr>

    <tr>

           <td>Boolean</td>

           <td>"false" if false, "true" if true</td>

    </tr>

    <tr>

           <td>Character</td>

           <td>string with the one character</td>

    </tr>

    <tr>

           <td>Function</td>

           <td>function name</td>

    </tr>

    <tr>

           <td>List</td>

           <td>list elements, separated by commas</td>

    </tr>

    <tr>

           <td>List.Method</td>

           <td>"list." with the method name appended</td>

    </tr>

    <tr>

           <td>Null</td>

           <td>null</td>

    </tr>

    <tr>

           <td>Number</td>

           <td>decimal form, or an exponential form if it's too large</td>

    </tr>

    <tr>

           <td>Method</td>

           <td>method name</td>

    </tr>

    <tr>

           <td>Object</td>

           <td>the return from Object.Name(), or the object's class</td>

    </tr>

    <tr>

           <td>Object.Method</td>

           <td>the return from Object.Name(), or the object's class,

                  followed by "." and the method's name</td>

    </tr>

    <tr>

           <td>Resource</td>

           <td>convert to string</td>

    </tr>

    <tr>

           <td>String</td>

           <td>NA</td>

    </tr>

    <tr>

           <td>String.Method</td>

           <td>"string." followed by the method's name</td>

    </tr>

    <tr>

           <td>String table</td>

           <td>the string designated for the current language</td>

    </tr>

    <tr>

           <td>Undefined</td>

           <td>""</td>

    </tr>

</table></p>

 

 

 

 

<p/><p><bold><big>String.Method</big></bold></p>

 

 

<p>

    A "String.Method" is a combination of a string and a method associated

    with that string. Although this is a dataype, programmers won't

    usually think of it as one since it's an intermediate step

    towards accessing the string's methods, such as String.StringSlice();

</p>

 

<p align=center><table bgcolor=#ffffff>

    <tr><td bgcolor=#80c0ff><bold>Convert data-type to string.method</bold></td></tr>

    <tr><td>No conversions possible.</td></tr>

</table></p>

 

 

 

 

<p/><p><bold><big>String table</big></bold></p>

 

 

 

<p>

    A string table is a reference to a string table (in the project).

    String table names are generated at compile time.

</p>

 

<p align=center><table bgcolor=#ffffff>

    <tr><td bgcolor=#80c0ff><bold>Convert data-type to string table</bold></td></tr>

    <tr><td>No conversions possible.</td></tr>

</table></p>

 

 

 

<p/><p><bold><big>Undefined</big></bold></p>

 

 

 

<p>

    The undefined type has no values.

</p>

 

<p align=center><table bgcolor=#ffffff>

    <tr><td bgcolor=#80c0ff><bold>Convert data-type to undefined</bold></td></tr>

    <tr><td>No conversions possible.</td></tr>

</table></p>

 

 

Get() and Set() code

Describes how to intercept get() and set() calls to global variables and properties.

 

<p>

    Typically, when a global variable or an object's property is

    created, the variable's contents can be accessed directly.

    Any function or method can change the variable (or method)

    to any value.

</p>

 

<p>

    You may wish to protect variables (or methods) so that only

    valid values can be written. Alternatively, you might

    need the accessing of a variable to run some code. (For example:

    The "weight" property of a backback might sum of the weight

    of its contents and its own weight.)

</p>

 

<p>

    You can do such interceptions by providing "get and set" code

    for the variable or property. There are two ways to do this:

</p>

 

<ul>

    <li>Check the "Use get/set code" in the variable's or property's definition</li>

    <li>Use some run-time calls to change the get/set code for variables or properties</li>

</ul>

 

 

<p/><p><bold><big>"Use get/set code" checkbox</big></bold></p>

 

<p>

    Providing your own get/set code for a global variable or

    an object's property is easy. If you are modifying a global

    variable you will find a checkbox for "Use get/set code" and

    one button each for "Get" and "Set. If you are modifying an

    object's properties, you'll find a similar checkbox and buttons.

</p>

 

<p>

    To provide your own get/set code:

</p>

 

<ol>

    <li>

           Check the <bold>"Use get/set code"</bold> checkbox.

    </li>

    <li>

           Press the <bold>"Get"</bold> button to modify the code to

           get the variable.

    </li>

    <li>

           Press the <bold>"Set"</bold> button to modify the code to

           set the variable.

    </li>

</ol>

 

<p>

    Note: When you attach get/set code to a variable or method,

    any reads from or writes to the variable will access the

    get/set code. Except, from within the code for handling get

    and set itself; this will be able to access the variable directly.

</p>

 

 

 

<p/><p><bold><big>Run-time calls</big></bold></p>

 

<p>

    Modifying the get/set code for a global variable or

    object's property at run-time is slight more difficult.

</p>

 

<p>

    To modify a global variable's get/set code you need

    to call <bold>GlobalGetSet()</bold>. See the function's definition

    for more information.

</p>

 

<p>

    To modify an object's property's get/set code you need to

    call <bold>LayerPropGetSetAdd()</bold>. See the

    method's definition for more information.

</p>

 

Load/save objects

Describes how to loading/saving objects from/to disk works.

 

<p>

    MIFL lets you save one or more (or all) objects to disk, and

    then reload them at a later point. The exact function needed

    to do this depends upon the context, so I can't go into

    details here.

</p>

 

<p>

    However, I can explain the basic process:

</p>

 

 

 

 

<p/><p><bold><big>Saving</big></bold></p>

 

<p>

    When one or more objects are saved, the following steps are taken

    for each object:

</p>

 

<ol>

    <li>

           <bold>Identify the initial properties in a virgin object</bold> - A "virgin"

           form of the object is created using the object's class (from the

           lowest-priority layer). The virgin object has its properties

           initialzed according to the "initialized to" values for

           the object's methods.

    </li>

    <li>

           <bold>Save only property changes</bold> - The to-be-saved

           object is compared to its "virgin" version. Only those properties

           that have been changed (or deleted) will be saved.

    </li>

    <p>

           This is a very useful feature since it not only reduces the

           space necessary to save objects, it makes it safer for authors

           to save all the objects, change some properties, and then

           reload the previous objects (which were created with old code).

           Since only changed (or deleted) properties were saved, the

           realoded objects will contain all the properties as prescribed

           by the new code, except for the changes.

    </p>

    <p>

           Changing the code between a save and load isn't 100% safe

           though, and problems can arise. For example: If a saved

           object relied on the class "Unhealthy", but when it reloaded

           discovered that no "Unhealthy" class exists, it will ignore

           the "Unhealthy" class.

    </p>

    <li>

           <bold>Save all layers</bold> - All the layers of the object

           are saved, including any single-instance methods and get/set

           code.

    </li>

    <li>

           <bold>Save contained in</bold> - The object reference of the

           object's container will be saved. The contained objects'

           references are not, however, because this information

           can be reconstructed from the containers.

    </li>

    <li>

           <bold>Save all timers</bold> - All the object's timers are

           saved. The timer's suspended state is also saved.

    </li>

</ol>

 

<p>

    In addition, if all the objects are saved, the following

    information is also saved:

</p>

 

<ol>

    <li>

           <bold>Global variable changes</bold> - Just as only changed

           (and deleted) properties are saved, so too with global variables.

           Any unchanged global variable is ignored.

    </li>

</ol>

 

<p>

    <bold>A note about numerical accuracy:</bold> - Because of the

    wave data save is currently implimented, numbers may be rounded

    off to the nearest 0.00001. This is not a problem for most

    numbers, but might cause problems under some circumstances.

</p>

 

 

<p/><p><bold><big>Loading</big></bold></p>

 

 

<p>

    If all the objects were saved, the following

    information is loaded:

</p>

 

<ol>

    <li>

           <bold>Global variable changes</bold> - All the global variables

           are created and set the the "initialized to" value from compile.

           Then, any changes are loaded from the saved version.

           If any globals were deleted prior to saving then they're

           deleted.

    </li>

</ol>

 

<p>

    When the saved objects are reloaded, the following steps happen

    for every object:

</p>

 

<ol>

    <li>

           <bold>Check for object ID conflict</bold> - If there is a conflict

           in the object's ID with an object that already exists, then

           a new ID is created. The object's ID is changed to the new ID,

           and any saved object (or data) that references the old ID gets

           updated.

    </li>

    <li>

           <bold>Create layers</bold> - The loaded object has all the

           proper layers setup.

    </li>

    <li>

           <bold>Create properties</bold> - All the properties (from the

           lowest priority layer) are added using the compiled

           "initialized to" values.

    </li>

    <li>

           <bold>Load property changes</bold> - Any changes to the

           properties are loaded. This also includes property deletions.

    </li>

    <li>

           <bold>Load contained in</bold> - The contained-in reference

           is loaded and the loaded object is placed in the container.

           If the container no longer exists then the loaded object

           is not placed in any container.

    </li>

    <li>

           <bold>Load all timers</bold> - All the object's timers are

           load. The timer's suspended state is also loaded.

    </li>

    <li>

           <bold>Call Constructor2</bold> - If the object supports

           a Constructor2() method, that is called so the object knows

           it has been loaded from a saved state.

    </li>

</ol>

Loop and stack limits

Talks about limits to loops and stack depth.

 

<p>

    MIFL performs the following checks to prevent infinite loops:

</p>

 

<ul>

    <li>

           <bold>Loop counts</bold> - For most contexts, MIFL will

           abort a loop if it is more than a million iterations.

           This protects against infinite loops.

    </li>

    <li>

           <bold>Call-stack depth</bold> - For most contexts, MIFL will

           abort a function or method call if it is more than 1000

           function/method calls deep. This protects against infinite

           recursion.

    </li>

</ul>

 

Documentation – Server Library

01 Database Overview

The database functions (DatabaseXXX) can be used to store large numbers of objects to disk.

<p><bold><big>Database functions</big></bold></p>

 

<p>

    The Circumreality server provides a set of database functions you can

    use to save objects (from your virtual machine) onto disk

    when they're not used. This saves memory and processing power.

</p>

 

<p>

    For IF, you can use the database if your world is too large

    to fit in memory, or you have objects that aren't needed

    all the time. For example: You may wish to save information

    about your players and their characters in separate databases,

    only loading in the information when the player has logged on.

    When the player logs off, the information is saved back to disk.

</p>

 

<p>

    The database is also useful because it saves to disk, which

    is good protection in case the server crashes. You can even

    have the database make daily backups to other directories

    on disk, or even other servers connected through a LAN.

</p>

 

 

<p><bold><big>Database categories</big></bold></p>

 

<p>

    The first issue you need to tackle when dealing with the

    database functions is to determine what database "categories"

    you'll need. Each category represents a separate list of

    objects stored in its own file.

</p>

 

<p>

    For IF, you'll probably want the following database categories:

    "Players", "PlayerCharacters", "SaveToDatabase", and "Objects".

    You may also want categories for "EMails", and "NewsgroupPosts"

    so that users can send message to one another.

</p>

 

<p>

    Database categories are automatically created when the category

    is referenced. You don't need to call any funtions.

</p>

 

 

 

 

 

<p><bold><big>Cached properties</big></bold></p>

 

<p>

    Each database category stores a list of objects. Each

    stored object retains its class and properties.

    When you wish to retrieve one or more items from a database

    you'll need to search the database (do a query) for objects

    whose properties match certain qualifications.

</p>

 

<p>

    For example: When a user types in their player name, your

    application will query the "Players" database for all objects

    whose "Name" matches the name typed in by the user.

    Likewise, when the user wishes to select a character, you'll

    need to query the "PlayerCharacters" database for all objects

    whose "Player" matches the player name.

</p>

 

<p>

    To make queries as fast as possible, the database "caches"

    one or more properties from the objects. The cached properties

    are usually those used for queries. While you could conceivably

    cache every property in all the objects, you shouldn't because

    that would make the cache information very large, defeating

    the memory-saving ability of the database.

</p>

 

<p>

    For example: From the "Players" database you would definitely

    cache "Name". You might also cache some properties useful for

    administration, like "TotalTimeLoggedOn". The "PlayerCharacters"

    database would cache "Name" and "Player". You might also

    cache "Level", "Race", "Class", etc. so you can quickly generate a list

    of all player characters along with their race and level.

</p>

 

<p>

    To add a property to be cached call DatabasePropertyAdd() with

    the database name and cached property as parameters.

    (Example: DatabasePropertyAdd ("Players", "Name");)

    Repeated calls add more properties. (If a property is already

    on the cached list then the call will just return TRUE

    for success, since the property is already cached.)

</p>

 

<p>

    One important thing to remember about cached properties is that

    if a property is added AFTER the database has objects, whenever

    that property it accessed for the pre-existing objects it will

    return Undefined. As the objects are checked out (accessed), however,

    the cache list will be updated with their correct values.

    What this means is that you should define what properties

    are to be cached before your databases are finalized.

</p>

 

 

 

 

 

 

<p><bold><big>Adding an object to the database</big></bold></p>

 

<p>

    To add an object to the database:

</p>

<ol>

    <li>

           Create it as normal, using "MyObject = new cMyClass" (or whatever).

    </li>

    <li>

           Call "DatabaseObjectAdd ();" with the database name and the

           object. Example: "DatabaseObjectAdd ("Players", MyObject);

    </li>

</ol>

 

<p>

    At this point the object will be saved to the database and

    immediately "checked out". An object that is checked out

    can only be accessed by the application that has it checked

    out (preventing another application from using the same player).

    Furthermore, an object that is checked out must be

    "checked in" for the changes to be saved.

</p>

 

<p>

    Once an object is added, use it as normal until your

    program is ready to delete the object. At that point

    the program needs to check in the object.

</p>

 

 

 

 

 

<p><bold><big>Checking in an object</big></bold></p>

 

 

<p>

    You can think of the whole process of checking out and checking

    in an object like a visit to the library... to find a book

    you first visit the card catalog (the database query). Once

    you know what book you want, you get it from the shelves and

    check it out from the librarian. From then on, only you have

    access to it. When you're finished with it, check it back in,

    and it goes back on the shelves and is available for other

    visitors.

</p>

 

<p>

    When your application is finished with a checked-out object,

    it should call "DatabaseObjectCheckIn()", passing in the

    database category and the object. This will save the object

    to disk and DELETE it from memory (just like

    calling "delete MyObject"). If you don't want the object

    deleted from memory then you can pass a parameter into

    DatabaseObjectCheckIn() that will cancel the deletion.

</p>

 

<p>

    If your object contains other objects (such as a player character

    holding possessions) then you MUST check in the contained

    objects first before checking in the container object.

    Conversely, when you check out, your MUST check out the container

    first, followed by the contained objects. If you do check out/in

    in the wrong order MIFL will not properly remember which object

    is contained within which other object.

</p>

 

<p>

    If you merely want to save your object to disk without checking

    it in, call "DatabaseObjectSave()" instead. You may wish to

    do this for two reasons: 1) If the computer crashes, all the

    information about an object will be lost unless it's saved (or checked

    in). 2) When an object is saved, any properties from the

    will be updated in the cache, allowing for more up-to-date queries.

</p>

 

 

 

 

<p><bold><big>Checking out an object</big></bold></p>

 

<p>

    You can check an object out of the database by

    calling "DatabaseObjectCheckOut()", using the database

    category name and the object.

    Checking an object out will load it off disk and create

    a version of the object in memory. (In many ways, the

    functionality is similar to a call to "new cMyClass" except

    that the newly created object has all the properties and

    timers of the checked in class.)

</p>

 

<p>

    Once an object is checked out you can use it as normal.

    When you're finished with it, check it back in.

</p>

 

<p>

    To check an object out, however, you need to know which

    object you want. This involves a trip to the card catalog (aka:

    a database query.)

</p>

 

 

 

 

 

<p><bold><big>Database queries</big></bold></p>

 

<p>

    To get a list of all the objects in the players database, just

    call: DatabaseQuery ("Players", NULL, '|');

    This will return a list of all the objects in the database.

    You can then use DatabaseObjectCheckOut() to get each

    object in turn, examinine it, and check it back in.

</p>

 

<p>

    If you wished to find the player object belonging to the

    player that's logging in, checking out and then checking in

    every single player object in the database would be very

    slow.

</p>

 

<p>

    There is a better way to do this: Call

    "DatabaseQuery ("Players", ["==c", "|Name", "Tim Jones"], '|');

    instead. This call will return a list of objects whose

    name (property) is "Time Jones".

</p>

 

<p>

    Here is an explanation of the paramters:

</p>

 

<ol>

    <li>

           The "Players" entry informs the query to look in the "Players"

           database.

    </li>

    <li>

           "["==c", "|Name", "Tim Jones"]" tells the query to find

           all objects whose "Name" (property) is equal ("==c") to "Tim Jones".

           The '|' in front of "Name" identifies it as a properties, as

           opposed to a string. ("Tim Jones" does NOT have a '|' in front of it.)

           The "==c" is a case-insensative comparison.

    </li>

    <li>

           The '|' parameter identifies what character is used to indicate

           that a string is actually a property name, as opposed to a string

           to compare against.

    </li>

</ol>

 

<p>

    The "==c" is called an operator. It specifies what kind of

    comparison to use when comparing the "Name" property to "Tim Jones".

    There are a couple dozen different operators (see the

    DatabaseQuery() documentation). For example: "!=c" would find

    all players whose name is NOT "Time Jones", while "containsc" would

    find all players whose name contains the string, "Tim Jones", so

    "Tim Jones III" and "Hittim Jones" would be returned from the

    query.

</p>

 

<p>

    You can also compare several properties (as explained in

    the DatabaseQuery() documentation). For example: To find

    all players named "Tim Jones" or whose name contains "Tim", pass

    in "["||", ["==c", "|Name", "Tim Jones"], ["containsc", "|Name", "Tim"]]".

    The "||" is a logical for the next two operands.

</p>

 

 

 

 

<p><bold><big>Avoiding checking out and checking in</big></bold></p>

 

<p>

    Sometimes your application will just need to get a few

    properties from an object (which may be cached) and doesn't

    really need to check out the object.

</p>

 

<p>

    For example: You may have done a query for all the

    objects whose name contains "Tim" and now want to display

    their names and ages on the screen. You could go through

    each object and check it out, get the property values, and

    then check the object back in. This would be slow.

    It also has a problem: If the player object was checked out

    elsewhere you wouldn't be able to access it.

</p>

 

<p>

    You could also call "DatabasePropertyGet()" to get the

    properties directly.

</p>

 

<p>

    Example:

</p>

 

<ol>

    <li>

           Call "ListOfTims = DatabaseQuery ("Players", ["containsc", "|Name",

           "Tim"], '|');" to fill "ListOfTims" with a list of all players

           named Tim.

    </li>

    <li>

           Call "ToDisplay = DatabasePropertyGet ("Players", ListOfTims, "Name");"

           to get the "Name" property from all the objects in "List of tims".

           This might return ["Tim Jones", "Tim Jones III", "Hittim"].

    </li>

</ol>

 

<p>

    If you wished to get both the "Name" and "Age" properties from

    the players in the list, you could always call DatabasePropertyGet()

    twice. Or, you wrap the property names into a list

    and call: "DatabasePropertyGet ("Players", ListOfTims,

    ["Name", "Age"]);" This will return a list of lists. The

    first level of the list will correspond to all the objects.

    The second level (lists within the main list) will be

    the values for the "Name" and "Age" properties respectively.

</p>

 

<p>

    If you just wished the name for the first Tim, calling

    "DatabasePropertyGet ("Players", ListOfTims[0], "Name");" would

    return "Tim Jones". (Notice the [0] index onto ListOfTims.)

</p>

 

<p>

    Be aware that while DatabasePropertyGet() will get properties

    for objects that are checked out, the property value may not

    be up-to-date. If an object is checked out and its "Name"

    property change, calls to DatabasePropertyGet() will return

    the old name. At least until the object is checked in, or

    the object is saved.

</p>

 

 

 

 

<p><bold><big>Avoiding checking out and checking in, part 2</big></bold></p>

 

<p>

    You can use a sister function, "DatabasePropertySet()" to change

    one or more properties without actually checking out (and then

    checking in) the object.

</p>

 

<p>

    NOTE: DatabasePropertySet() will NOT be able to modify an

    object that is checked out. It can only modify objects

    that are checked in. (DatabasePropertyGet() can access properties

    from objects that are checked out or in.)

</p>

 

<p>

    Example: To change a player's name without checking it out,

    call "DatabasePropertySet ("Players", ListOfTims[0], "Name",

    "Tim's new name");"

</p>

 

<p>

    Just as DatabasePropertyGet() can accept a list of objects

    to get (as opposed to just one object), or a list of

    properties to get (as opposed to just one property), so

    to can DatabasePropertySet(). For more information see

    the DatabasePropertySet() documentation.

</p>

 

 

 

 

 

<p><bold><big>Deleting checked-out objects</big></bold></p>

 

<p>

    If you <bold>delete</bold> a checked-out object using

    "delete XXX", or "XXX.DeleteWithContents()", or

    "DeleteGroup()", then if the object is checked out, it

    will automatically be deleted from the database. If

    it is <bold>not</bold> checked out, then its database

    entry will be unaffected.

</p>

 

02 Saved Games

The SavedGameXXX() functions can be used to save a game, or group of objects.

<section><p><bold><big>Saved games</big></bold></p></section>

 

<p>

    You can use the SavedGameXXX() functions to load or save

    a game (for a single player game), or to create instanced

    areas of the game.

</p>

 

<p>

    To save a game:

</p>

 

<ol>

    <li>

           If you have values of <bold>global variables</bold> that

           you wish to save, you need to put the global variables

           into a named object. Or better yet, design your IF title

           so it doesn't use global variables to store information

           that might change across game saves.

    </li>

    <li>

           <bold>Determine every object</bold> that needs to be

           saved. You don't need

           to worry about child objects since they can be handled

           automatically.

    </li>

    <p>

           If most of the objects need to be saved (such as for saving

           an entire game), then determine which objects <bold>don't

           need to be saved</bold>. For example: You won't wish to

           save the connection objects because if you do, reloading the

           saved game will lose connection information.

    </p>

    <li>

           Call <bold>SavedGameSave()</bold>, passing in a <bold>game

           name</bold> and <bold>the list of objects</bold> you wish

           to save (or exclude from saving). The function accepts

           a flag indicating whether or not you wish to save all objects

           and exclude from the list, or save only in the list.

           It also accepts a flag which causes all the children (and

           their children, recursive) to be saved.

    </li>

</ol>

 

<p>

    That's it. To reload the game:

</p>

 

<ol>

    <li>

           Call <bold>SavedGameLoad()</bold> with the name of the game

           and a flag indicating if you wish to remap objects.

    </li>

    <p>

           If remapping is set to TRUE then any objects you load that

           already exist will be given new IDs (and the old ones will

           be kept around).

    </p>

    <p>

           If remapping is FALSE then loaded objects

           will replace any old ones.

           If the game was saved with the SaveAll flag

           set to TRUE then any deleted objects will also be deleted.

    </p>

</ol>

 

<p>

    You can also call <bold>SavedGameEnum()</bold> to list

    saved games, or <bold>SavedGameRemove()</bold> to delete

    saved games.

</p>

 

<br/><section><p><bold><big>Advanced saved games</big></bold></p></section>

 

<p>

    When you save a game, you need to specify both a <bold>file name</bold>, and

    a <bold>sub-file name</bold>.

</p>

 

<p>

    The file name corresponds to an actual

    file written to disk, in a sub-directory of where the databases are stored.

    For example: The "MySaves" filename would be translated

    into "[DatabaseDir]\SavedGames\MySaves.msg".

</p>

 

<p>

    The sub-file is written within the main .msg file.

</p>

 

<p>

    The advantage of this system is that instances (see below) for players

    on a multiuser game can be easily saved. Each character can be assigned

    a main file name (probably based on their character's object ID). All

    of the instanced maps are saved as sub-files within the character's

    save-game file.

</p>

 

<br/><section><p><bold><big>Creating an instanced dungeon</big></bold></p></section>

 

<p>

    <bold>NOTE:</bold> Instances are handled by the Basic Interactive Fiction

    library. If you want to use instances, you should use the functions and

    methods provided there. However, you may find this section interesting

    because (a) it explains the basic principles, and (b) it lets you rewrite

    the code in the Basic Interactive Fiction library.

</p>

 

<p>

    An instanced dungeon (or area of the world) is a copy of the

    world that can only be accessed by select PCs (usually friends).

    A section of the world may be instanced several times over

    so that many groups can play in multiple copies of the same area.

</p>

 

<p>

    To create an instanced dungeon:

</p>

 

<ol>

    <li>

           <bold>Build the section of your world</bold> that will

           be instanced, and note what rooms are in it. Keep

           the <bold>list of rooms in a global</bold>.

    </li>

    <li>

           Make sure there are <bold>no direct entrances</bold> into

           the instanced area so that players and monsters cannot

           just walk in.

    </li>

    <li>

           Provide a <bold>door with custom code</bold> that will redirect

           each player to the right instance of the world. When the

           PC walks through the door, it figures out which instanced

           world should be used and calls ActorMove() to move the player

           to that world.

    </li>

    <li>

           The rooms should always be kept <bold>sterile</bold>, which means no

           player characters or NPCs from outside can enter, and nothing from within

           can leave. They reason they're sterile is because they act as a template

           for all the instances.

    </li>

    <li>

           When a player enters an instanced set of rooms, and the rooms need to be

           created, call <bold>SavedGameEnum()</bold> to see if the player already

           has entered the instanced rooms.

    </li>

    <p>

           If this is the first time the player is entering the instance,

           call <bold>ObjectClone()</bold> to clone all the rooms and their

           contents. The player will be allowed to enter the copies of the

           cloned rooms.

    </p>

    <p>

           If the player has entered, the call <bold>SavedGameLoad()</bold> to load

           in the instanced rooms. This will loaded in the instanced rooms that,

           at some point, were created using ObjectClone().

    </p>

    <li>

           When a player needs to be moved into the instance, <bold>find

           the room</bold> to move them into using the remapping list,

           searching for the old room and returning the new one.

    </li>

    <li>

           That's all you need to do, except when <bold>all the players</bold> leave

           the instanced dungeon and are finished with it,

           call <bold>SavedGameSave()</bold> and delete all of the

           instanced rooms and their contents.

    </li>

</ol>

 

 

 

03 Online Help

Describes the online help system.

<section><p><bold><big>Online help</big></bold></p></section>

 

<p>

    Creating online help for a Circumreality title is easy. All you need

    to do is create a number of "Help" resources; they will

    automagically be placed into a table of contents and indexed.

</p>

 

<p>

    To create a help resource:

</p>

 

<ol>

    <li>

           Select the <bold>Add new resource</bold> option from

           the <bold>Misc</bold> menu,

    </li>

    <li>

           In the "Add a resource" page that appears,

           press the <bold>"Help"</bold> button.

    </li>

    <li>

           In the "Modify resource" page, type in

           the help topic name, like <bold>rHelpGetCommand</bold>. The

           resource name should be unique for each help topic.

    </li>

    <li>

           Press <bold>"Add"</bold> to add a resource for a specific

           language.

    </li>

    <li>

           In the "Help resource" page that appears, type

           in a <bold>Name</bold> that will be visible to the players.

           This name must also be unqiue for each help topic.

    </li>

    <li>

           Type in the <bold>Category</bold> in which the help topic

           will be placed (for the table of contents). To place

           the help topic at the top of the TOC, leave this blank.

    </li>

    <p>

           The TOC is like a directory tree. If you wish to place

           the help topic several sub-directories down, just use a

           forward slash ('/') to separate them. For example:

           "Plants/Trees/Evergreen", will create a "Plants" category

           at the top of the TOC. If the user clicks on that they

           will see the "Trees" category. Underneath that is the

           "Evergreen" category, and that contains the article

           for pine trees.

    </p>

    <li>

           You can place the article in a <bold>secondary category</bold> if

           you wish, or leave the entry blank.

    </li>

    <li>

           Enter a <bold>short description</bold> for the category like,

           "An article about pine trees".

    </li>

    <li>

           Next you need to enter the <bold>MML text</bold> for the category.

           MML is a lot like HTML (used for web pages). If you have

           never used HTML before, look up some reference manuals

           on the Internet, or look at some other help topics to see

           how the format works.

    </li>

    <p>

           Press the <bold>Test MML</bold> button to make sure you don't

           have any mistakes in your MML, and that it looks good.

    </p>

</ol>

 

<p>

    Those are the basics. When you next run your Circumreality title the

    help article will automatically be added to the table of contents

    and indexed.

</p>

 

 

 

<br/><section><p><bold><big>Advanced online help</big></bold></p></section>

 

<p>

    At the bottom of the help-resource page are some options

    that might come in useful:

</p>

 

<ul>

    <li>

           The <bold>Function</bold> and <bold>Function parameter</bold> fields

           are useful for any help topics that should only be seen

           by administrators or characters with specific skills.

    </li>

    <p>

           If you fill in "Function" with a function name, the function

           will be called if the user tries to access the help topic.

           The first parameter for the function will be the actor searching

           for help, and the second will be the "function parameter" (as

           a string.) If your function returns TRUE, the player will be

           allowed to see the help topic. Returning FALSE will hide the

           help topic from the player.

    </p>

    <li>

           The <bold>Book</bold> field allows you to divide help into

           several books, and only show topics for a specific book.

           While this functionality is not usually needed, it does come

           in handy for role-playing knowledge.

    </li>

    <p>

           For example: You could

           write several help topics about Elvish culture that the player

           would only have access to if they played an Elf character (or

           learned some sort of skill). Then, you could create a new

           command that mimiced the "Help" command, but was designed

           for role-playing knowledge, like a "Do I know" command.

           If the user had Elvish role-playing knowledge they would

           be given access to the help in the "Elvish" book.

    </p>

</ul>

 

 

 

 

<br/><section><p><bold><big>Online help functions</big></bold></p></section>

 

<p>

    Some useful online help functions are:

</p>

 

<ul>

    <li>

           <bold>HelpArticle()</bold> searches for a specific help

           article and returns the MML resource (that can be sent to

           the player's computer), along with the categories the

           help topic is in.

    </li>

    <li>

           <bold>HelpArticleMML()</bold> just returns the MML

           resource. It automatically includes links at the end of the

           page so the player can see the TOC where the article is

           placed.

    </li>

    <li>

           <bold>HelpContents()</bold> returns a list of all the

           articles and sub-directories given a directory in the

           table of contents.

    </li>

    <li>

           <bold>HelpContentsMML()</bold> returns a MML resource

           with all the contents information from HelpContents().

    </li>

    <li>

           <bold>HelpSearch()</bold> performs a keyword search.

    </li>

    <li>

           <bold>HelpSearchMML()</bold> returns a MML resource from

           a keyword search.

    </li>

</ul>

 

04 Automatically downloaded data and files

A little bit of information about automatically downloaded data and files.

<section><p><bold><big>Automatically downloaded data and files</big></bold></p></section>

 

<p>

    When Circumreality produces a .crf file, it automatically scans all the

    resources for file references, such as .wav files, and includes them

    in the .crf file. That way, any data file referenced in a resource

    will be included in the .crf file for distribution.

</p>

 

<p>

    If the IF title is run multiuser over the Internet, then any

    file in the .crf file will be transferred over as its needed.

</p>

 

<p>

    There are some exceptions to this... the Circumreality client's install

    will install a few files that are commonly used in all Circumreality

    titles. These include MikeRozak.tts (text-to-speech

    voice), EnglishInstalled.mlx (lexicon for text-to-speech),

    and LibraryInstalled.me3 (basic 3d objects).

    The Circumreality client will <bold>not download</bold> these files from

    the server.

</p>

 

<p>

    Furthermore, if a 3rd party application copies a .tts or

    or .mlx file into the client application's directory then it

    will use those rather than installing them.

</p>

 

<p>

    What this means:

</p>

 

<ul>

    <li>

           If you have a more recent (or older) version of the .tts or

           .mlx files listed, you cannot guarantee that your versions

           will be used. (The LibraryInstalled.me3 won't be any problem

           though; it will update properly.) Usually, this is not

           a problem.

    </li>

    <li>

           The user may have installed a high-quality TTS voice over

           the default voices, resulting in better sounding TTS.

           A high quality TTS voice is 50+ megabytes, and not something

           most people want to download.

    </li>

</ul>

 

<p>

    See also the "Binary database", since files in the binary database

    are automatically downloaded.

</p>

05 Binary database

Shows how to use the binary database to store images that players upload.

<section><p><bold><big>Binary database</big></bold></p></section>

 

<p>

    Players may wish to upload images of their characters so that whenever

    another player sees their character, the image will automatically

    be downloaded. Likewise, sounds could be customized by the players.

</p>

 

<p>

    The <bold>binary database</bold> lets you accomplish this. The database

    just stores binary data accessed by a file name, such as "test file.jpg".

    Various functions are provided to add/remove data to the database,

    see below. Once data is in the database, <bold>any automatic download

    requests from the client</bold> will access the database, as well

    as precompiled binary information. Thus, any reference to "test file.jpg"

    on the client will automatically pull the data from the binary database.

</p>

 

<p>

    The binary database supports the following methods:

</p>

 

<ul>

    <li>

           <bold>BinaryDatabaseEnum()</bold> - Enumerates all entries in the

           database, or all entries beginning with a specific prefix.

    </li>

    <li>

           <bold>BinaryDatabaseGetNum()</bold> - Gets the name of a database

           entry based on the index into the database.

    </li>

    <li>

           <bold>BinaryDatabaseLoads()</bold> - Loads binary data from the

           database.

    </li>

    <li>

           <bold>BinaryDatabaseNum()</bold> - Returns the number of entries

           in the database.

    </li>

    <li>

           <bold>BinaryDatabaseQuery()</bold> - Returns the size and modification

           dates for the data.

    </li>

    <li>

           <bold>BinaryDatabaseRefresh()</bold> - Sends a message to all the connected

           clients that a file in the database has been changed, and that they should

           reload it.

    </li>

    <li>

           <bold>BinaryDatabaseRemove()</bold> - Deletes an entry from the binary

           database.

    </li>

    <li>

           <bold>BinaryDatabaseRename()</bold> - Renames a database entry.

    </li>

    <li>

           <bold>BinaryDatabaseSave()</bold> - Saves binary data into the database.

    </li>

</ul>

 

 

 

06 Text/event logging

Describes how to log text into the text log.

<section><p><bold><big>Text/event logging</big></bold></p></section>

 

<p>

    Circumreality has a "text log" that can be used to store gigabytes of

    log information, such as when users log on, log off, what actions

    they take, etc. Logging is vitally important for finding bugs as

    well as detecting players that cheat.

</p>

 

 

<br/><section><p><bold><big>Log everything</big></bold></p></section>

 

<p>

    As a general rule, log everything, or at least, provide the ability

    to log everything.

</p>

 

<p>

    Categorize each event into one of four levels:

</p>

 

<ul>

    <li>

           <bold>Very important (Category 1)</bold> - These are events that you

           always want to log, such as when your code realizes that its data

           is corrupt, or even that a user has logged on/off.

    </li>

    <li>

           <bold>Important (Category 2)</bold> - Category 2 events will always

           be logged too, but they're not as critical. For example: Log commands

           that the user types.

    </li>

    <li>

           <bold>Nice to know (Category 3)</bold> - Category 3 events are only logged

           if you are suspicious about a specific user, or you set the system

           to an elevated state of alert. For example: A character picks up

           or drops an item.

    </li>

    <li>

           <bold>Unimportant (Category 4)</bold> - These events are only logged

           if a user is marked as being very suspicious or the system is set

           to a very high state of alert. For example: Storing all the MML sent

           from the server to the clients.

    </li>

</ul>

 

<p>

    In you code, you should check

    either <bold>gTextLogUser[EVENTLEVEL]</bold> or <bold>gTextLogSystem[EVENTLEVEL]</bold> before

    logging an event. If the value is TRUE then log the event.

    Use gTextLogUser[] if the event pertains to an action from the specific

    user. gTextLogSystem[] is for events that are independent of the user,

    such as something a NPC does.

</p>

 

<p>

    There are three ways to log an event:

</p>

 

<ul>

    <li>

           <bold>TextLog()</bold> - This logs a string, as well as an optional object.

           The current user, the user's character, and the location of the user's character

           are also logged. You will probably use TextLog() more often than the other

           two logging functions.

    </li>

    <li>

           <bold>TextLogNoAuto()</bold> - Like TextLog(), this logs a string, but

           it lets you specify which user object, character object, room object,

           and object are associated with the log. Use TextLogNoAuto() if, for example,

           a NPC acts.

    </li>

    <li>

           <bold>Trace()</bold> - Calling Trace() will only log the text line. You

           won't be able to store additional information in the log, such as

           the user or room.

    </li>

</ul>

 

<p>

    Combining, these two together, a sample line of logging code might

    look like:

</p>

 

<p align=center><table width=80%>

    <tr><td>

           if (gTextLogUser[3])<br/>

           &tab;TextLog ("Pick up", vObjectThatPickedUp);

    </td></tr>

</table></p>

 

 

 

<br/><section><p><bold><big>Some more details</big></bold></p></section>

 

<p>

    The globals, gTextLogUser and gTextLogSystem, are intializes by

    calls to <bold>TextLogPriorityAdjust()</bold>. TextLogPriorityAdjust() is

    called in the connection object when a message arrived from a user.

    It does the following:

</p>

 

<ol>

    <li>

           Adjusts <bold>gTextLogUser and gTextLogSystem</bold>. These

           values are affected by <bold>gTextLogAlert</bold>, which controls the

           overall system alert, and ther user's <bold>pUserLogAlert</bold> which

           controls what priority messages are to be logged for a specific user.

           Increase gTextLogAlert to record lower priority (higher value) events

           for all users. Increase pUserLogAlert to record lower priority (higher

           value) events for a specific user, such as one that might be cheating.

    </li>

    <li>

           The function also calls <bold>TextLogAutoSet()</bold> to tell

           the text log what user, room, and player character to automatically

           use when TextLog() is called.

    </li>

</ol>

 

<p>

    It is unlikely that you will need to call TextLogPriorityAdjust() directly,

    but knowing of its existence helps you understand what's going on.

</p>

 

 

 

<br/><section><p><bold><big>Accessing the text log</big></bold></p></section>

 

<p>

    Circumreality provides several functions that are useful for accessing the

    text log database:

</p>

 

<ul>

    <li>

           <bold>TextLogAutoGet() and TextLogAutoSet()</bold> - Let you set the

           user, character, and room that are used by TextLog().

    </li>

    <li>

           <bold>TextLogDelete()</bold> - Deletes one of the text logs files.

           Circumreality creates one new file every hour. Circumreality includes code that automatically

           deletes logs older than a few days, affected

           by <bold>gTextLogDeleteOld</bold>.

    </li>

    <li>

           <bold>TextLogEnableGet() and TextLogEnableSet()</bold> - Completely disable

           text logging. These are used when Circumreality is running in an offline, single-player

           mode, and logging isn't warranted.

    </li>

    <li>

           <bold>TextLogEnum()</bold> - Lists all of the log files, one per

           hour that the world has been running.

    </li>

    <li>

           <bold>TextLogNumLines()</bold> - Returns the number of events logged

           in specific text log file.

    </li>

    <li>

           <bold>TextLogRead()</bold> - Reads in a line/event from a specific

           text log file.

    </li>

    <li>

           <bold>TextLogSearch()</bold> - Searches through the text log files

           for all events that occur within a specific date/time range, for

           a specific user, character, room, or object, and with a specific

           sub-string.

    </li>

</ul>

 

Documentation – Basic interactive Fiction Library

Provides basic interactive fiction definitions, classes, objects, and functions.

 

The Basic Interactive Fiction library provides definitions, classes, objects, and functions that are necessary for interactive fiction to work. Using this library is highly recommended, since without it, you'll have to write your own.

01 How command parsing works

Describes how a command typed in by a user is processed.

 

<section><p><bold><big>How command parsing works</big></bold></p></section>

 

<p>

    This tutorial describes how a command, like "pick up

    the sword", is received by the

    server, processed, and then acted upon.

</p>

 

<section><p><bold><big>From the server</big></bold></p></section>

 

<p>

    Commands from the client are sent to the appropraite connection

    object. (Usually a cConnection.) The ConnectionMessage()

    method is called with the command message.

</p>

 

<p>

    Assuming that the user isn't in a specific state (like logging

    in), the ConnectionMessage() function eventually passed

    the message down to a handler that calls two functions:

</p>

 

<ol>

    <li>

           <bold>CommandParse()</bold> - This parses the command and

           determines which parse makes the most sense, based upon

           where the player's character and other inff.

    </li>

    <li>

           <bold>CommandAct()</bold> - Once the best choice is determined,

           the command is acted upon.

    </li>

</ol>

 

<p>

    Simple... except that a lot is done in these two functions.

</p>

 

 

<section><p><bold><big>NLPParse()</big></bold></p></section>

 

<p>

    When the oParserVerb() object (in the Basic IF Library) initializes,

    it loads a few commonly used NLPRuleSets into the "Commands"

    parser. These are rParserVerbRuleSet and rNLPCommon. Let me explain.

</p>

 

<p>

    Circumreality comes with some built in utility functions for helping

    parse commands and speech passed to a chatterbot. The

    built in functions are all accessed from the NLPParserXXX()

    and NLPRuleSetXXX() functions. You may wish to look

    through their documentation for complete instructions,

    but here's an overview:

</p>

 

<p>

    The built-in parser functions accept a string from the

    user, such as "pick up the lantern", breaks it into

    individual words ("pick", "up", "the", "lantern") and

    then uses some NLP rules to figure out what the

    command means.

</p>

 

<p>

    The NLP rules are actually very simple (and primitive).

    All they do is match a series of words and convert them

    to a new series of words. In the case of parsing "pick up the lantern",

    there is a rule someplace in rParserVerbRuleSet that

    converts "pick up" to "`get". (Note the '`' in front

    of "get". It tells code later-on down the pipe

    that "`get" is a langauge-indepent result, and is <bold>not</bold> a

    word typed in by the user.)

    A temporary rule (see below) will convert "the lantern"

    into "|0123456789abcdef0123456789abcdef" (or some other GUID),

    indicating the exact object that's referenced.

</p>

 

<p>

    The parser ends up producing a list of hypothesis.

    One entry is the original, "pick up the lantern", with a score of

    1.0. One entry is the correct one, "`get |GUID" (where GUID is

    that really long number that identifies the object).

    There are also other, less conclusive parses, like "pick up |GUID"

    and "`get the lantern", which will be discarded later.

</p>

 

<p>

    The function doing the parsing is NLPParse(). It returns

    all the hypothesis in a list.

</p>

 

 

<section><p><bold><big>CommandParse() call</big></bold></p></section>

 

<p>

    The CommandParse() call eventually calls into NLPParse(). However,

    it first does some other work...

</p>

 

<p>

    CommandParse() enumerates all the objects that are near the

    user, using ObjectsEnumNearby(). oParserVerb is added onto

    this list even though it technically isn't nearby.

</p>

 

<p>

    Each nearby object is asked for any one-off rules that need

    to be added to the list of rules passed into NLPParse().

    This is done by calling NLPRuleSetTemp(), which

    in turns calls NLPRuleSetTempName() and NLPRuleSetTempVerbs().

</p>

 

<p>

    NLPRuleSetTempName() will return a rule that converts the

    object's name, such as "the lantern" or "lantern" or even

    "the lamp" into the "|GUID" string. Since every nearby

    object is queried for a name, the call ensures that

    the parser will handle the names.

</p>

 

<p>

    Some objects will also support NLPRuleSetTempVerbs(), which

    identifies verb-rules that are only available when the

    object is nearby. In the example below about moving

    a chess piece on a chessboard object, the rule

    might be to convert "move" to "`MOVECHESSPIECE".

</p>

 

<p>

    Finally, temporary rules are added for pronouns, so that

    "it" will refer to the last object referenced, etc.

    See NLPPronounGet() for more information.

</p>

 

<p>

    The user's command ("pick up the lantern"), along

    with the temporary rules, are passed into NLPParse(),

    and a list of hypothesis is returned. Each hypothesis

    has the possible meaning (such as "`get |GUID") and

    a score.

</p>

 

<p>

    Each nearby object is asked if it supports its own command

    parser. If it does, it will return either 1 or 2 from

    the NLPCommandParseQuery() call. Most objects <bold>will not</bold> support

    their own parser. The reason they would is if having the

    nearby allowed for some new commands. For example: If a chessboard

    were nearby, a user could type "Move CHESSPIECENAME to LOCATION".

    The normal verb parser in oParserVerb doesn't know about

    chess board moving, but because the object is around, suddenly

    the parser understands chessboard commands.

</p>

 

<p>

    oParserVerb returns 2 from a NLPCommandParseQuery() to indicate

    that it wants to parse commands.

</p>

 

<p>

    Once CommandParse() knows all of the nearby objects that want

    to parse commands. It then procedes to send <bold>all</bold> of

    the hypothesis to <bold>all</bold> of the flagged objects and

    asks them if they can make heads or tail of the command.

    This call is made through NLPCommandParse().

</p>

 

<p>

    If a parser can understand the command, it will return a

    confidence score along with a callback that will take action

    on the parse, and parameters to be passed into the callback.

</p>

 

<p>

    The returned confidence score is multiplied by the score for

    the hypothesis. The final hypothesis chosen is the one

    with the highest total score resulting from the multiplication.

    This way, the confidence score allows one NLPCommandParse()

    call to make a higher "bid" than others, ensuring that it

    gets to act upon the command.

</p>

 

<p>

    The winner (highest score) is returned from CommandParse()

    and then passed into CommandAct(). The information

    about the winner includes the callback and parameters

    supplied by NLPCommandParse().

</p>

 

 

 

 

<section><p><bold><big>CommandAct() call</big></bold></p></section>

 

<p>

    CommandAct() checks to see if there was a winner from

    CommandParse(). If there wasn't one, it sends a message back

    to the player saying that the command wasn't understood.

</p>

 

<p>

    If there is a winning parse, its callback is called, and the

    parameters it requested are passed into it. The callback

    then does whatever action is deems necessary.

</p>

 

 

<section><p><bold><big>oParserVerb</big></bold></p></section>

 

<p>

    While any object can providing a parser, most of the

    parsing is done in the oParserVerb object. This section

    will go into detail about how it works.

</p>

 

<p>

    When NLPCommandParse() is called for a hypothesis,

    such as "`get |GUID", the oParserVerb object will remove

    the '`' character from the first token (in this case "`get")

    and then prepend "Parse_". Therefore, "`get" is turned

    into "Parse_get", "`look" into "Parse_Look", etc.

</p>

 

<p>

    If the oParserVerb object supports the Parse_Get (or Parse_Look, etc.)

    method then

    that is called, passing in exactly the same parameters

    as were passed into NLPCommandParse(). If Parse_Get is

    not supported then NLPCommandParse() returns without

    setting a callback.

</p>

 

<p>

    Tip: If you wish to add new verbs to the command parser,

    just add them the oParserVerb object. First, produce rules

    in a NLPRuleSet resource that convert the verb into a token.

    (For example: To allow jumping, you'd make a rule that converts

    "Jump" or "Leap" to "`jump".) You'll need to call NLPRuleSetAdd()

    with your resource whenever the IF session starts.

    Then, add a new public method to oParserVerb called "Parse_Jump".

    Have it parse the command (see below), and return

    information about the parse. That's all.

</p>

 

<p>

    Back to the call into Parse_Get()...

</p>

 

<p>

    Parse_Get() knows that the first token is "`get". All it has

    to do is look at whatever follows and determine what should

    happen if this command is actually acted upon.

</p>

 

<p>

    First, lets assume a valid command was passed in: It

    would be "`get" followed by an object (from "|GUID").

</p>

 

<p>

    Parse_Get() would first figure out a score. If the object

    was visible to the actor (see ObjectCanSee()), and

    accessible (sse ObjectCanAccess()), and not too heavy to

    pick up, then the score would be 1.0. However, if any of

    these conditions was not met, the score would be lower, such as

    0.1.

</p>

 

<p>

    This way, if there are two lanterns, only one of

    which can be picked up, and the user

    types "get lantern", then the lantern which can be picked up

    will have a score of 1.0, while the one that can't will have

    a score of 0.1. The higher score will be chosen for the command,

    so the lantern which can be picked up will be taken. However,

    if the only lantern around can't be picked up, its callback

    will be run, and will tell the user that he can't reach the lantern.

</p>

 

<p>

    Sometimes the hypothesis already has an acceptable parse.

    If Parse_Get() has produced a lower score than what's already

    there then it just returns. If its score is higher, Parse_Get()

    will overwrite the existing parse with its own.

</p>

 

<p>

    Next, Parse_Get() needs to provide a callback and parameters

    to pass to it. For convention's sake, the callback will

    be Act_Get(), and the parameters depend upon whether or

    not the checks to see if the lantern could be reached succeded.

</p>

 

<p>

    If the player can pick up the lantern, then the parameter might

    just be the lantern object. If the player can't pick it

    up (because it's on a high shelf, or is nailed to the ground)

    then the parameters would be the object and some sort of value

    indicating that it couldn't be picked up and why.

</p>

 

<p>

    ParseGet() returns its score, callback, and callback parameters.

    It then waits for the callback to be called. (The callback might

    not be, since there may be better parses.)

</p>

 

<p>

    When/if the callback is called, it can look at the parse parameters

    and act accordingly. In the case of Act_Get(), it would

    move the object to the player's possession and alter the user,

    or maybe inform the user that the object couldn't be moved.

</p>

 

<p>

    NOTE: Act_Get() needs to call NLPPronounSet() to ensure that

    the pronouns such as "it" and "he" are modified to the

    most-recently referenced objects.

</p>

 

 

<section><p><bold><big>oParserVerb - Tips and tricks</big></bold></p></section>

 

<p>

    If you want to occasionally override a verb handled

    by oParserVerb, but only for certain types of objects,

    there are two options:

</p>

 

<ol>

    <li>

           You can write a custom parser for each object that has

           the special get code. This was explained above.

    </li>

    <li>

           You can create a new method in oParserVerb to handle

           the extra verb. See below...

    </li>

</ol>

 

<p>

    To handle the alternate form of the verb by modifying

    oParserVerb:

</p>

 

<ol>

    <li>

           Create a NLPRuleSet resource that converts the tokenized

           form of the verb, such as "`get" to "`MySpecialGet" with

           a probability of 99. (As close to 100% as you can get.)

    </li>

    <li>

           Make sure to load this rule set into the "Commands" parser

           when the program stars up. The easiest way to do this

           it to have an automatically-created object whose Constructor()

           and Constructor2() load it in.

    </li>

    <li>

           Add Parse_MySpecialGet() to oParserVerb. If it does successfully

           parse it will need to return a score higher than 1.0 to

           ensure that its parse it used instead of the one

           from Parse_Get().

    </li>

</ol>

 

 

02 Noun cases and noun-verb agreement

Describes useful functions for dealing with noun cases and noun/verb agreement.

 

<section><p><bold><big>Noun cases and noun-verb agreement</big></bold></p></section>

 

<p>

    Online fiction generates a lot of text (to be displayed or spoken)

    by splicing together two strings. For example: When a PC picks

    up and object, such as a lantern, the Circumreality code will

    need to splice together "You" + "pick up" + "a lantern" + ".".

    However, at the same time, Circumreality may need to display the string

    to other players in the room, in which case it becomes

    "Fred Smith" + "picks up" + "a lantern" + ".". Notice how

    not only does the name change from "You" to "Fred Smith", but

    the verb changes from "pick" to "picks". This is the crux

    of the problem... it's called noun cases (going from "You" to

    "Fred smith") and noun/verb agreement ("pick" to "picks").

</p>

 

 

<br/><section><p><bold><big>Getting an object's name</big></bold></p></section>

 

<p>

    So if you have an oLantern object, how do you get its name,

    "the shiny lantern"?

</p>

 

<p>

    The easiest way to get an object's name is just to concatenate

    the object to a string, such as "You pick up " + oLantern;

    However, this doesn't give the system much to work with. For example,

    it's not sure if you want "the" or "a" before the lantern's

    name, or if you want the full (long) name "shiny lantern" or

    a the short one ("lantern"). As a result, getting an object's

    name using this method will work, but you won't be able to control

    the results.

</p>

 

<p>

    Similarly, you could call oLantern.Name(); it would return

    the same string as was appended above.

</p>

 

<p>

    The correct way to get an object's name is to use the

    NLPName() method. It allows you to specify some paramters

    that enable a better name display:

</p>

 

<p>

    The first (optional) parameter is the actor's name. The actor

    is the one looking at the object. Most of the time this won't

    make any difference. However, if the actor is the same as the

    object, then NLPName() will automatically display the

    correct pronoun. Thus oLantern.NLPName(oLantern, ...) will

    automatically return "you", or whatever is appropriate for

    the current language.

</p>

 

<p>

    The second (optional) parameter is a bit-field that lets you

    control what form of the object's name should be displayed;

    it is "a shiny lantern", "the lantern", or "10 lanterns".

    The flags are global variables of the form gNC_XXX_YYY.

    You "or" them together, like: (gNC_Art_Definite | gNC_Count_Many

    | gNC_Caps_Upper).

    Some ones which you may find useful:

</p>

 

<ol>

    <li>

           <bold>gNC_Art_Definite</bold> causes "the" to be

           displayed. <bold>gNC_Art_Inefinite</bold> causes "a" or "an"

           to be displayed. <bold>gNC_Art_None</bold> shows the

           name without any articles.

    </li>

    <li>

           <bold>gNC_Caps_Upper</bold> will capitalize the first

           character in the same, useful for the start of a sentence.

    </li>

    <li>

           <bold>gNC_Case_Subjective</bold> causes pronouns to use

           the subjective case ("I", "he", "she"),

           while <bold>gNC_Case_Objective</bold> causes pronouns to

           be objective ("me", "him", "her"). You can

           display the possessive form ("The lantern's")

           using <bold>gNC_Case_Possessive</bold>. A whole

           host of gNC_Case_YYY flags are available for langauges

           other than English.

    </li>

    <li>

           <bold>gNC_Case_Single</bold> causes the singular version

           of the object to be displayed, such as

           "lantern". <bold>gNC_Case_Few</bold> and <bold>gNC_Case_Many</bold> will

           show the plural form, "lanterns". Some languages have

           two definitions of plural, "few" (either 2 or 3) and "many" (4+).

           That's why there are two plural cases. If your interactive

           fiction will only every be English then use either one.

    </li>

    <li>

           <bold>gNC_Gender_Male</bold>, <bold>gNC_Gender_Female</bold>,

           and <bold>gNC_Gender_Nuter</bold> will control the gender

           of the object's string. In English, this usually only affects

           pronouns such as "he" and "she".

    </li>

    <li>

           <bold>gNC_Verbose_Long</bold> and <bold>gNC_Verbose_Short</bold> let

           you specify the use of the object's short string ("lantern") or

           long string ("shiny lantern"). Not all objects will differentiate though.

    </li>

</ol>

 

 

<p>

    You only need to use a flag if the default result isn't

    right. Usually the defaults will be correct.

</p>

 

<p>

    The third parameter (also optional) is a boolean that specifies

    whether the "noun-case" string is to be appended. If this

    is TRUE or not set, a number surrounded by brackets will be

    appended to the object's name. This number stores the noun-case

    information (such as plural or singular), and is useful later

    on for noun/verb agreement. If you will eventually pass this

    string on to NLPStringFormat() to do noun/verb agreement,

    then you'll want the noun-case string added.

</p>

 

<p>

    If you wish to display an object being possessed by another

    object, such as "Mike's lantern", then use the NLPNamePossessed()

    call. It not only saves you some work, but it makes localization

    to other languages easier. For example: Spanish does not use

    's to identify possession, but instead uses "the lantern of Mike".

    The NLPNamePossessed() function will automatically handle such

    changes.

</p>

 

 

<br/><section><p><bold><big>Creating objects that display the right name</big></bold></p></section>

 

<p>

    If you create an object in MIFL and then call the NLPName()

    method, you'll be sorely disappointed. First of all, it will

    display the object's name, such as "oLantern" as the actual

    string. Second, if your object's name is in any way

    irregular, such as "person" vs. "people" (singular and plural forms)

    then NLPName() won't work properly.

</p>

 

<p>

    To make NLPName() produce the right string, you may need to change

    some of your object's properties, and maybe even an occasional

    method:

</p>

 

<p>

    First off, you will need to modify <bold>pNLPNounName</bold> to

    your object's name, such as "lantern". (Note: Keep the first

    character lower case unless it's a proper name and you always

    want it capitalized.) Be aware that pNLPNounName is

    different than <bold>pNLPParseName</bold>, which is used to

    identify the object in a command; see "How command parsing

    works" for a description of pNLPParseName.

</p>

 

<p>

    If the name has a long vs. short form, you'll need to use

    some special tags within pNLPNounName. In this example, to

    create a long name of "shiny lantern", you'll

    need to set pNLPNounName to "(long?shiny )lantern".

</p>

 

<p>

    The parenthsis in the string identify that the noun-case needs

    to be tested. The "long" part before the '?' chacter indicates

    that the gNC_Verbose_Long setting is to be tested. If it

    is set then the "shiny " string is placed before "lantern".

</p>

 

<p>

    You could also make the short version be "lamp" by

    changing the string to "(long?shiny lantern:lamp)".

    The characters after the colon (and before the last parenthesis)

    are used it gNC_Verbose_Long is not set.

</p>

 

<p>

    The string, "(lon?shiny lantern:lamp") would also work.

    Notice that "long" has been shortened to "lon". You can

    abbreviate the noun case to two to four letters depending upon

    the name. It cannot be so short that it's indistinguishable

    from another name. For example: There is also a "short" test to

    see if the short form of the noun is desired (which is the opposite

    of the "long" test) and a "single" test to see if the noun

    is sinugular. You can't abbreviate "short" down to just "s" because

    then the computer wouldn't know if you meant "short" or "single".

</p>

 

<p>

    You can use a similar technique if the noun has irregular

    plurals. For example: To show "hippopotumus" when one hippo

    is around, and "hippopotumi" when several around about,

    you would set the object's pNLPNounName to

    "hippopotum(plur?i:us)". The "plur" test checks for a plural

    noun case. If pNLPNounName handles the plural case,

    set <bold>pNLPNounNoAutoPlural</bold> to TRUE.

</p>

 

<p>

    But that's not all...

</p>

 

<ol>

    <li>

           Some languages, like French, differentiate between animate and

           inanimate objects. To indicate that your object is

           animate, change the <bold>pNLPNounAnimate</bold> property.

    </li>

    <li>

           To indicate that your noun is plural by default,

           change the <bold>pNLPNounCount</bold> property. For example:

           You'd use this object was "pants".

    </li>

    <li>

           To change the gender of your object,

           use <bold>pGender</bold>. This is particularly

           useful for pronouns. Objects default to the

           neuter gender.

    </li>

    <li>

           If you never want to prepend an article ("a", "an", or "the")

           in front of your object's name,

           set <bold>pNLPNounNoAutoArticle</bold>. You'll want to do

           this for proper names, since "the Mike" or "a Mike" is not

           standard English. You can also use this, along with

           the noun-case tests in pNLPNounName to create

           custom aritcles, such as "(indef?lots of:(defi?the)) gold".

    </li>

    <li>

           Possessive forms of an object automatically have "'s" or "s'"

           added. If you would rather handle the possessive case

           in pNLPNounName, then set <bold>pNLPNounNoAutoPossessive</bold> to

           TRUE.

    </li>

    <li>

           To always display a quantity in front of the noun,

           set <bold>pNLPNounQuantity</bold> to TRUE. For example, if

           you have a stack-of-gold object, you may wish to have it

           always display the number of coins in it. You may

           also wish to write your own NLPNounQuantity() method for

           the object.

    </li>

</ol>

 

 

<br/><section><p><bold><big>Noun/verb agreement</big></bold></p></section>

 

<p>

    As shown at the beginning of this tutorial, your will encounter

    situations where a noun string and a verb string are concatenated

    together, and you need to conjugate the verb to match the noun...

    "I am...", "You are...", "He is...", "We are...", "You(pl) are...",

    and "They are...".

</p>

 

<p>

    Circumreality provides some functions that make this easy...

</p>

 

<p>

    You've already seen the first set using the NLPName() method.

    It automatically appends a number surrounded in brackets. This

    number indicates the number and person (1st, 2nd, or 3rd) of

    the noun. For example: oLantern.NLPName() might return

    "The shiny lantern{3543434}".

</p>

 

<p>

    When you use a verb that relies upon a concatenated noun

    string (which will have the "{number}" appended), you

    must write out the three verb forms in parenthesis

    and separated by a '/'. For example: "(am/are/is) in good shape.".

</p>

 

<p>

    The first form is used for the 1st person singular ("I").

    The second is used by the 2nd person singular and all plural forms

    ("you", "we", "they"). The third entry is for

    the 3rd person sinular ("he" or "she").

</p>

 

<p>

    Then the noun string and verb string are concatenated they will

    look something like this: "The shiny lantern{3543434} (am/are/is)

    in good shape."

</p>

 

<p>

    If you pass this string to NLPVerbForm(), it will return

    "The shiny lantern is in good shape.". It uses the number in

    curly braces to determine which form the verb should take.

</p>

 

<p>

    An even better way of displaying the string is to use

    the NLPStringFormat() function, which is a combination

    of the StringFormat() method and NLPVerbForm(). It

    works by replacing "%1", "%2", etc. in the string to the given

    argument, and then passing the concatenated string to

    NLPVerbForm().

</p>

 

<p>

    Example: NLPStringFormat("%1 (am/are/is) in good shape.",

    oLantern.NLPName(Actor, gNC_Caps_Upper | gNC_Art_Definite));

    Will produce the same results.

</p>

 

<p>

    NLPStringFormat() is the recommended solution for concatenating

    nouns and verbs since it also makes localizing easier.

    Not all languages use the same word order, so the "%1", "%2", etc.

    allow the order of insertion to be flipped depending upon the

    language.

</p>

 

 

 

 

<br/><section><p><bold><big>Advanced</big></bold></p></section>

 

<ul>

    <li>

           You might wish to look at the <bold>NLPPronoun()</bold> function. This will

           return a pronoun string, such as "he", "she", or "it" based

           upon the noun-case information passed in.

    </li>

    <li>

           There is more to an object's name than just pNLPNounName and

           pNLPNParseName, especially when dealing with characters.

    </li>

    <p>

           You may wish to set <bold>pNLPNameReal</bold> and <bold>pNLPNameRealParse</bold> with

           the character's name, such as "Bill Smith". Or, you might

           even with to write your own code for <bold>NameReal()</bold>.

    </p>

    <p>

           If your character has an obvious profession, such as "innkeeper" or

           "guard", then provide

           a <bold>pNLPNameProfession</bold> and <bold>pNLPNameProfessionParse</bold>,

           or write your own <bold>NameProfession</bold>.

    </p>

    <p>

           If you have races, such as cRaceElf, then you will need to

           write your own <bold>NameRace()</bold>.

    </p>

    <p>

           In fact, if your character has a NameRace(), you don't even need

           to provide a pNLPNameReal or pNLPParseName!

    </p>

</ul>

 

 

03 cObject – Generic objects

Covers the cObject, a generic object class that's a superclass of all other MIF objects.

 

<section><p><bold><big>cObject - Generic objects</big></bold></p></section>

 

<p>

    The cObject class is at the heart of all the interactive

    fiction objects, such as lanterns, monsters, player characters,

    doors, and rooms. Every other interactive fiction object

    is based directly or indirectly from cObject.

</p>

 

<p>

    This tutorial will give an overview about how to use cObject.

    You should read this before learning how to use cRoom for rooms,

    cCharacter or cMonster for monsters, NPCs, and player characters, cDoor

    for making doors, cContainer for containers, etc. since they're

    all based on cObject.

</p>

 

 

<br/><section><p><bold><big>Making a new object</big></bold></p></section>

 

<p>

    To make a new object for your world, such as a lantern:

</p>

 

<ol>

    <li>

           Create a new object using the <bold>"Objects" menu, and

           selecting "Add a new object"</bold>.

    </li>

    <li>

           <bold>Type in the name for the object;</bold> if it's an object in

           the world (such as a specific lantern) then prefix the

           name with a "o" so it's easy to identify it as such.

           If it's a class, such as a "light emitting class" then

           prefix it with "c".

    </li>

    <li>

           Under the object's <bold>"Superclass" tab</bold>, press

           the <bold>"Add class"</bold> button.

    </li>

    <li>

           In the "Add class(es)" page, check the <bold>"cObject"</bold> class.

           This tells MIFL that your lantern object is based off

           the generic object.

    </li>

    <li>

           Press <bold>"Back"</bold> to return to the classes page;

           you'll see the "cObject" class listed.

    </li>

    <li>

           Check the <bold>"Automatically create an an object"</bold> button;

           this is what makes it an object and <bold>not</bold> a class.

    </li>

    <li>

           Below that, <bold>select</bold> the room that the lantern will

           appear in. If it starts out held or contained by another

           object then select that object.

    </li>

    <li>

           <bold>Don't bother</bold> changing the object's GUID since

           a unique one has automatically been generated.

    </li>

    <li>

           Switch to the <bold>properties</bold> tab.

    </li>

    <p>

           When you selected the cObject class, some properties were

           automatically added. These include pNLPParseName, pNLPNounName,

           and pVisual.

    </p>

    <li>

           Type in <bold>pNLPParseName</bold> and <bold>pNLPNounName</bold>.

           These are the names used to identify the object when its name

           is typed in a command, and how to diplay the name to the

           user. For more information on these,

           see the <bold>How command parsing works</bold> and <bold>Noun

           cases and noun-verb agreement</bold> tutorials.

    </li>

    <p>

           Some commands, particularly in conversations, allow the use of

           possessives, like as "Bill Smith's". Circumreality already contains code

           to create possessives in NLPMakePossessive(), but if this

           ends up making a mistake then you may need

           to provide your own <bold>pNLPParseNamePossessive</bold>.

    </p>

    <li>

           For <bold>pVisual</bold> you'll need a resource that

           is to be used to "draw" a picture of the object. It

           can either be an Image, ThreeDScene, ThreeDObject, Title,

           or Text resource. If you don't have one now, leave this

           blank and a default visual will be provided.

    </li>

    <li>

           You should set <bold>pWeightSelf</bold> to the weight of

           the object, in kilograms. <bold>pVolumeSelf</bold> is

           the area that the object takes up, in liters. If the object

           is roughly as dense as water, use the same values for both.

           However, if the object is light but large, like a pillow,

           you'll need a high volume.

    </li>

    <li>

           Since you want your lantern to provide light,

           add the <bold>pProvidesLight</bold> property and set

           it to <bold>TRUE</bold>. Of course, this means that

           your lantern is always on, but it's good enough

           for the purpose of this tutorial.

    </li>

    <li>

           If you want your lantern to be described the first

           time a player sees it, set <bold>pDescribed</bold>.

    </li>

</ol>

 

<p>

    Assuming you had a room to place the latern in, you should

    be able to compile the project and visit the room with

    your character. The lantern will be there and providing light.

</p>

 

 

<br/><section><p><bold><big>Properties and methods</big></bold></p></section>

 

<p>

    The default cObject supports numerous properties and methods.

    A typical object will only need to override some of the

    properties and methods. For example: The lantern object

    needs to change <bold>pProvidesLight</bold> and the methods

    dealing with turning the object on or off.

</p>

 

<p>

    For more details on the supported properties and methods,

    look around the help files.

</p>

 

 

04 cRoom – Room objects

How to make rooms and connect them.

 

<section><p><bold><big>cRoom - Room objects</big></bold></p></section>

 

<p>

    In MIFL, rooms are just objects (based on cObject) that players

    can walk around in. If you want, you could even design backpacks

    that players could walk around in; it's all the same. A room

    is basically a container that's large enough to hold player

    characters. (Although the room class is <bold>not</bold> based

    on the cContainer class.)

</p>

 

 

<br/><section><p><bold><big>Creating a new room</big></bold></p></section>

 

<p>

    Creating a room is easy:

</p>

 

<ol>

    <li>

           Just as described in the "cObject" tutorial, <bold>create

           a new object</bold>, but <bold>instead</bold> of deriving

           your room off cObject, derive it from <bold>cRoom</bold>.

           (cRoom is a subclass of cObject, so by deriving from cRoom

           you are also deriving from cObject.)

    </li>

    <p>

           If you want to make a <bold>grid of rooms</bold>, such as 16 x 16 rooms

           for a section of wilderness, then name your rooms like,

           "oRoomWildernessXXxYY". Replace XX with the X (east/west) location in the

           16 x 16 grid, and YY with the Y (north/south) location.

           However, offset your numbers so that the room in the center of the

           3D model (.m3d file) is at 50 x 50. Thus, the center room would

           be oRoomWilderness50x50. The room to the west of it would be

           oRoomWilderness49x50.

    </p>

    <p>

           If all of the rooms in the grid are basically

           the same, you can save yourself time by creating the room in the

           north-west corner first, oRoomWilderness42x42, and then copying the

           same object. Everytime you copy a room, the last number, Y, will be

           automatically incremented by one, to "oRoomWilderness42x43", then

           "oRoomWilderness42x44". One you've created 16 rooms at 42xYY, you'll

           need to manually enter a 43 for the X location.

    </p>

    <li>

           Make sure the <bold>Automatically create as an object</bold> button

           is checked.

    </li>

    <li>

           Rooms <bold>do not need</bold> to start off contained in

           other objects, although it's possible.

    </li>

    <li>

           In the object's <bold>properties</bold> tab, you'll find a

           number of properties that were automatically added when

           you selected the "cRoom" class. You'll need to fill these in.

    </li>

    <li>

           <bold>pNLPParseName</bold> and <bold>pNLPNounName</bold> will

           need to be filled in. See <bold>How command parsing

           works</bold> and <bold>Noun cases and noun-verb agreement</bold>.

    </li>

    <li>

           <bold>pAutoMapMap</bold> should be filled in the the

           map that the room is in. For now, use oMapDefault for

           the value. I'll discuss maps, regions, and zones later.

    </li>

    <li>

           <bold>pLocation</bold> controls

           where the center of the room is in the map. This is a list

           of [EW, NS, UD], where EW is the location in meters east (positive)

           or west (negative). NS is north (positive) or south (negative).

           UD is up (positive) or down (negative).

    </li>

    <p>

           You <bold>don't have to set pLocation</bold> if your room is

           part of a grid of rooms. The X and Y locations will automatically

           be converted into a pLocation, using pAutoMapMap.pMapRoomSeparation

           to determine the room spacing. The default spacing is 10 meters.

    </p>

    <li>

           <bold>pDimensions</bold> specifies the size of the room.

           It is a list with [EW, NS, UD], where EW is the east/west

           size in meters, NS is north/south, and UD is the

           height. You might

           also want to change some of the other automap settings (see

           the help) to control the room's shape or orientation.

    </li>

    <p>

           You <bold>don't have to set pDimensions</bold>. If you don't,

           the dimensions will be guessed based on the sizes of the

           surrounding rooms.

    </p>

    <li>

           <bold>pVisual</bold> and <bold>pVisualDark</bold> provide

           the image that the user sees when he/she enters the room.

           You should probably use a ThreeDScene resource with a 360

           degree view for a room. If you haven't produced a 3D model

           yet, leave pVisual and pVisualDark blank. (<bold>pVisualDark is

           optional</bold>; it is used to draw the room when there's no light,

           but will be automatically simulated by the lighting models.)

    </li>

    <p>

           Alternatively, you can <bold>leave the room's pVisual blank</bold> and

           create a <bold>pVisual for the room's pAutoMapMap</bold>. In

           this case, the room's visual will automatically be based on

           the map, and the camera location will be set to the

           room's <bold>pLocation</bold>. An empty pVisual is particularly

           useful for a grid of rooms.

    </p>

    <p>

           You'll ususally use a <bold>ThreeDScene</bold> resource with

           a 360-degree camera. Unless you

           change <bold>pRoomAutoCameraHeight</bold>, the camera height

           that you set in the ThreeDScene resource will be ignored,

           and it

           will automatically be adjusted based on the terrain or building (block)

           that the character is in. This is desirable most of the time,

           but may cause some confusion once in awhile when the camera

           height suddenly changes.

    </p>

    <li>

           If you want your room to be described the first

           time a player enters it, set <bold>pDescribed</bold>.

    </li>

</ol>

 

 

 

<br/><section><p><bold><big>Adding exits to rooms</big></bold></p></section>

 

<p>

    Once you have built your room you need to add some exits (and

    hence entrances.)

</p>

 

<ol>

    <li>

           In the "Properties" tab, add a <bold>pExitXXX</bold> property,

           like pExitNorth,

           where XXX is "North", "South", "East", etc. There are twelve

           different directions to choose from.

    </li>

    <p>

           If your room is part of a <bold>grid of rooms</bold> then exits

           to other rooms in the grid will be <bold>automatically generated</bold>.

           You will still have to set up exits to non-grid rooms. And, if

           you <bold>don't want an automatic exit in a grid room</bold> then

           set pExitXXX to NULL.

    </p>

    <p>

           Room exits will automatically be created as hotspots in the

           360-degree room image <bold>unless</bold> you

           change <bold>pRoomAutoHotSpots</bold>. The size and location

           of the hotspots are automatically calculated, although you

           can override this by changing <bold>pExitNorthHotSpot</bold> (or

           pExitXXXHotSpot), or writing

           your own <bold>RoomExitHotSpot()</bold> method.

    </p>

    <li>

           In the property's value, <bold>type in the name of the room</bold> you

           wish to connect it to, such as <bold>oRoomCircular</bold>.

    </li>

    <li>

           You will also need to modify oRoomCircular and provide an

           exit that connects to your new room.

    </li>

</ol>

 

<p>

    That's all. Easy, eh? If you wish to add doors,

    read the <bold>cDoor - Door objects</bold> tutorials.

</p>

 

 

 

<br/><section><p><bold><big>Spawning objects</big></bold></p></section>

 

<p>

    This is a bit of an advanced topic, but I'll cover it now since

    I'm covering rooms.

</p>

 

<p>

    Online virtual worlds (and some single player games) requires

    that monsters and items "spawn" from time to time. This means

    that new copies of monsters and items are automatically created

    a few minutes after the old ones are killed or taken. This

    is necessary for muliplayer games to ensure that there are

    still monsters and loot around even after previous players have been through.

</p>

 

<p>

    To have a room spawn monsters (or items):

</p>

 

<ol>

    <li>

           Set the <bold>pSpawnClass</bold> property to a string with

           the spawned monster's class, such as "cOrc". Or, to spawn

           magic mushrooms, use "cMushroomMagic". You can also use

           a list, to ensure that several orcs appear in the

           room, like ["cOrc", "cOrc", "cOrc"].

    </li>

    <li>

           Set the <bold>pSpawnTime</bold> to the average number

           of <bold>minutes</bold> between spawns. If you want

           an exact time, or want to control how the time varies,

           then use a list with [min time, max time].

           If you don't set the value, the default pSpawnTime for

           rooms is 10 minutes.

    </li>

    <p>

           See also <bold>pSpawnTimeRange</bold> to limit the times.

    </p>

    <li>

           If you wish the objects to spawn in an area (as opposed

           to always appearing in this room), or you wish them

           to appear in a container (such as a letter appearing in

           a mailbox), then change <bold>pSpawnLocation</bold>. Look

           at its documentation for details.

    </li>

</ol>

 

 

 

 

 

<br/><section><p><bold><big>Other properties and methods</big></bold></p></section>

 

<p>

    Rooms support many other properties and methods, such as properties

    for controlling ambient sounds. To find out more, look

    in the documentation.

</p>

 

<ul>

    <li>

           If you want to play a special cutscene when a player enters the room,

           then set <bold>pRoomEnterDisplay</bold> or write

           your own <bold>RoomEnterDisplay()</bold> method. This is particularly

           useful when a player enters a room and you wan't to narrate a special

           entrance, and have the NPC provide a special greeting.

    </li>

</ul>

 

 

<br/><section><p><bold><big>Maps, regions, and zones</big></bold></p></section>

 

<p>

    A large virtual world can contain tens of thousands of rooms.

    Because this many rooms is too conceptually difficult to fit

    onto one map, the rooms in a virtual world are grouped together.

    First, a collection of rooms is grouped into a "map", such as

    a map of a house, a map of a city, or a map of a dungeon.

    A collection of nearby maps is a "region". Regions might

    include counties, with multiple cities and towns.

    A collection of

    nearby regions is a "zone". Zones could include entire

    countries, containing multiple counties.

    Thus, every map is in a region, and

    every region in a zone.

</p>

 

<p>

    The IF library includes a default map, region, and zone

    object: oMapDefault, oRegionDefault, oZoneDefault. You can

    use these objects if you only want one map, one region, or

    one zone in your world. However, if you want more you'll

    need to create new objects.

</p>

 

<p>

    To make a map object:

</p>

 

<ol>

    <li>

           <bold>Create</bold> a new object,

    </li>

    <li>

           Set its super-class to <bold>cMap</bold>.

    </li>

    <li>

           Make sure the <bold>Automatically create as an object</bold> button

           is checked.

    </li>

    <li>

           In the object's properties page, fill

           in <bold>pNLPNounName</bold> and <bold>pNLPParseName</bold> with

           the map's name, such as "New York City".

    </li>

    <li>

           Fill in <bold>pAutoMapRegion</bold> with the region where

           the map is, such as oRegionDefault, or whatever regions you

           create.

    </li>

    <li>

           Fill in <bold>pLocation</bold> with [EW, NS, UD].

           These are <bold>offsets</bold> for

           the center of the map, compared to the center of the region.

    </li>

    <p>

           The same properties in a room control the room's location in the

           map. Likewise, in a map object, they control the map's location

           within the region. You <bold>must</bold> fill these in properly

           or the artificial intelligence routines will have difficulty

           walking from rooms on one map to another.

    </p>

    <li>

           If your map has waterfalls, or other objects that can be

           heard several rooms away, you might wish

           to set <bold>pMapAmbient</bold>.

    </li>

</ol>

 

<p>

    To create a <bold>region object</bold>, follow basically the

    same actions as a map object, but base the object off

    the <bold>cRegion</bold> class, and fill in <bold>pAutoMapZone</bold> instead

    of pAutoMapRegion.

</p>

 

<p>

    A <bold>zone object</bold> can be created in the same way,

    but is based off <bold>cZone</bold> and doesn't need a

    pAutoMapZone or pAutoMapRegion.

</p>

05 cDoor – Door objects

Tells you how to make doors and place them in rooms.

 

<section><p><bold><big>cDoor - Door objects</big></bold></p></section>

 

 

<p>

    The cDoor class is used to create doors that connect rooms.

    You don't need to connect rooms with doors, but if you

    want an obstacle (such as a locked door)

    between the rooms, you'll need to add a cDoor.

</p>

 

<p>

    If you haven't read the tutorial on <bold>cRoom - Room

    objects</bold> then you should do that now.

</p>

 

 

<br/><section><p><bold><big>Creating a new door</big></bold></p></section>

 

<p>

    Here's how you create a door:

</p>

 

<ol>

    <li>

           Just as described in the "cObject" tutorial, <bold>create

           a new object</bold>, but <bold>instead</bold> of deriving

           your room off cObject, derive it from <bold>cDoor</bold>.

           (cDoor is a subclass of cObject, so by deriving from cRoom

           you are also deriving from cObject.)

    </li>

    <li>

           Make sure the <bold>Automatically create as an object</bold> button

           is checked.

    </li>

    <li>

           Underneath the checkbox, <bold>select the room</bold> that

           the door will be in. (A door is usually "in" two rooms at

           once, since it straddles the two. I'll get to this later. For

           now just pick one of the rooms.)

    </li>

    <li>

           In the object's <bold>properties</bold> tab, you'll find a

           number of properties that were automatically added when

           you selected the "cDoor" class. You'll need to fill these in.

    </li>

    <li>

           <bold>pNLPParseName</bold> and <bold>pNLPNounName</bold> will

           need to be filled in. See <bold>How command parsing

           works</bold> and <bold>Noun cases and noun-verb agreement</bold>.

           Generally, pNLPParseName will be something like "[east] door",

           and pNLPNounName "east door". You can provide more exciting

           names, of course, but most people will expect doors to be called

           "door".

    </li>

    <p>

           Note: <bold>You don't have to</bold> fill in pNLPParseName

           and pNLPNounName for doors since a name will automatically

           be creasted by the door's exit within the room, such

           as "northeast door".

    </p>

    <li>

           <bold>pDoorCounterpart</bold> will be filed in later. I'll

           explain below.

    </li>

    <li>

           Doors do not usually have a <bold>pVisual</bold> property

           because they aren't evey drawn (as a separate object in the

           room) since they usually appear in the 3D model of the room.

           If you want your door to be drawn as an object separate from

           the room you'll need to provide a pVisual and

           set <bold>pDontListInRoom</bold> to FALSE.

    </li>

    <li>

           Now, <bold>switch to the room object</bold> that contains

           the door.

    </li>

    <li>

           Add a <bold>pExitXXXDoor</bold> property, where XXX is the

           wall that the door is on, such as pExitEastDoor. Type

           in the door's name, such as oDoorEast.

    </li>

</ol>

 

<p>

    You're not done though. You have only created half a door.

    Doors provide an unusual dilemma since they're an object that

    must appear in two rooms at once, which isn't possible

    with MIFL. Therefore, you need to create two door objects,

    one for each room its in.

</p>

 

<p>

    You have already created the first door object. Now you need to

    create the second:

</p>

 

<ol>

    <li>

           <bold>Return</bold> to the door object and

           switch to the <bold>Misc</bold>.

    </li>

    <li>

           Press the <bold>Duplicate this object</bold> button.

           This does exactly what it says.

    </li>

    <li>

           Modify the door's duplicate, chaning the <bold>room</bold> it

           appears in, and the <bold>ID</bold>... you don't need to make

           an entirely new number. Just change the rightmost digit.

    </li>

    <li>

           In the "Properties" tab, change the

           door's <bold>pNLPNounName</bold> and <bold>pNLPParseName</bold>,

           usually changing "east" to "west", etc.

    </li>

    <li>

           In <bold>both</bold> the doors, add

           the <bold>pDoorCounterpart</bold> property. The value for

           each should be the door's opposite. Setting this property

           ensures that when one (half of the) door is opened, so is

           the other (half of the) door.

    </li>

    <li>

           Visit the object definition for the <bold>other room</bold>,

           and set the <bold>pExitXXXDoor</bold> to the duplicate door.

    </li>

</ol>

 

<p>

    That's it. You now have two doors that appear as one.

</p>

 

<p>

    Normally you'd leave the duplication to the last moment, after

    you had set and extra door properties, such as the ability

    to lock it. By duplicating after all the lock properties have

    been added you save yourself some work.

</p>

 

 

 

<br/><section><p><bold><big>Locked doors</big></bold></p></section>

 

<p>

    To create a locked door:

</p>

 

<ol>

    <li>

           In the door's property tab, add the <bold>pLock</bold> property.

           Set it to <bold>TRUE</bold>. This defaults to the door being

           locked... which makes sense from a game point-of-view.

    </li>

    <li>

           Also add the <bold>pLockShape</bold> tab, and type in

           a string the identifies the shape of key that will

           open the door, such as <bold>"SkeletonKey12"</bold>. Some

           keys may fit to several shapes, allowing them to open

           several types of locks, just as master keys do. Conversely,

           you can have a door accept several key shapes by

           setting pLockShape to a list, like <bold>["SkeletonKey12",

           "SkeletonKey54"]</bold>.

    </li>

    <li>

           If you want your door to automatically lock itself after

           it has been unlocked, set <bold>pLockAutoLock</bold> to

           the number of seconds delay before it locks. This feature

           is extremely handy for multi-user interactive fiction,

           causing the door puzzle to reset. If you use automatic

           locking you may also wish to set <bold>pOpenAutoClose</bold> to

           have the door automatically close itself... a locked

           door does not good if it's left open.

    </li>

    <li>

           You'll need to make the <bold>exact same changes</bold> to

           the door's other half, unless you want different behavior

           for each side.

    </li>

    <li>

           You'll need to create a key object that

           supports <bold>pKeyShape</bold> with <bold>"SkeletonKey12"</bold>.

           See the tutorial, <bold>cObject - Generic objects</bold> for

           information on general-purpose objects.

    </li>

</ol>

 

<p>

    That's it. Now, players (and NPCs) will have to unlock the

    door before walking through it.

</p>

 

 

 

<br/><section><p><bold><big>Other properties and methods</big></bold></p></section>

 

<p>

    Doors support some other properties and methods, such

    as ways to make the doors see-through. For more information

    see the documentation.

</p>

06 cCollection – Collection objects

Collects are objects that are grouped together, such as gold, sand, and liquids.

 

<section><p><bold><big>cCollection - Collection objects</big></bold></p></section>

 

 

<p>

    The cCollection class lets you create objects that are

    groups of objects, such as gold, water, sand, and a deck of

    cards. The collection object allows you to combine collections

    (such as two piles of gold), split collections (one pile

    of gold into two), and it displays a quantity in front of

    the object (such as "52 gold pieces" or "5 liters of water").

</p>

 

<p>

    If you haven't read the tutorial on <bold>cObject - Basic

    objects</bold> then you should do that now because

    cCollection objects are based off cDoor objects.

</p>

 

 

<br/><section><p><bold><big>Creating a collection object</big></bold></p></section>

 

<p>

    Here's how you create a collection object:

</p>

 

<ol>

    <li>

           Just as described in the "cObject" tutorial, <bold>create

           a new object</bold>, but <bold>instead</bold> of deriving

           your room off cObject, derive it from <bold>cCollection</bold>.

           (cCollection is a subclass of cObject, so by deriving from cCollection

           you are also deriving from cObject.)

    </li>

    <li>

           Make sure the <bold>Automatically create as an object</bold> button

           is checked. (Note: If you are going to have more than one pile

           of gold, you may want to make a cCollectionGold class, and then

           derive each individual pile from that. This tutorial assumes

           you only have one pile of gold though.)

    </li>

    <li>

           Underneath the checkbox, <bold>select the room</bold> that

           the collection will be in.

    </li>

    <li>

           In the object's <bold>properties</bold> tab, you'll find a

           number of properties that were automatically added when

           you selected the "cCollection" class. You'll need to fill these in.

    </li>

    <li>

           Set the <bold>pVisual</bold> property, of course.

    </li>

    <li>

           <bold>pNLPParseName</bold> and <bold>pNLPNounName</bold> will

           need to be filled in. See <bold>How command parsing

           works</bold> and <bold>Noun cases and noun-verb agreement</bold>.

           Generally, pNLPParseName will be something like "[(pile|piece|pieces) [of]] gold [piece|pieces]",

           and pNLPNounName "gold piece". Unless you change pNLPNounQuantity,

           a number will always be displayed in front of "gold piece".

    </li>

    <li>

           Fill <bold>pQuantityUnits</bold> in with a character indicating

           what type of units are associated with the collection.

           Use 'm' if the object is grouped in meters (like a rope),

           '2' if it's square meters (like cloth), 'l' if it's in liters

           (liquids), 'f' if it's a fractional unit (like modern currency),

           or 'i' if it's an integer unit (like pieces of gold).

    </li>

    <li>

           If you have any type of units (besides the integer units) then

           set <bold>pQuantityMin</bold> to indicate the smallest size

           the object can be divided into. If you don't, some user

           will divide 100 liters of water into 100,000 objects, each

           with 1 ml.

    </li>

    <li>

           Set <bold>pQuantity</bold> to the number of such units.

           In the case of gold, it will be the number of gold coins.

    </li>

    <li>

           Set <bold>pCollectionType</bold> to a string that uniquely

           identifies the class of object that this collection holds.

           In this case, it would be "goldpieces". If you had

           a water object you'd use "water". If you have healing potion

           it would be "healingpotion". This string is necessary to make

           sure that only collections of the same type can be merged

           together.

    </li>

</ol>

 

<p>

    That's it. If you play with your new object you'll notice the

    following feature:

</p>

 

<ul>

    <li>

           The name always displays the quantity in front of it.

    </li>

    <li>

           You can split the object into smaller parts, using

           a command like "divide gold into 3 parts".

    </li>

    <li>

           You can combine piles of gold togehter with

           a command, "combine all gold", or "combine 3 gold pieces with

           5 gold pieces" (assuming you have one pile with 3 gold peices,

           and another with 5 gold pieces).

    </li>

</ul>

 

07 cContainer – Container objects

Describes how to make containers, like backpacks and chests.

 

<section><p><bold><big>cContainer - Container objects</big></bold></p></section>

 

 

<p>

    The cContainer object lets you easily make containers, such

    as bags, backpacks, chests, hat racks, book cases, etc.

</p>

 

<p>

    If you haven't read the tutorial on <bold>cObject - Basic

    objects</bold> then you should do that now since

    containers are based off basic objects.

</p>

 

 

<br/><section><p><bold><big>Creating a container</big></bold></p></section>

 

<p>

    Here's how you create a container:

</p>

 

<ol>

    <li>

           Just as described in the "cObject" tutorial, <bold>create

           a new object</bold>, but <bold>instead</bold> of deriving

           your room off cObject, derive it from <bold>cContainer</bold>.

           (cContinaer is a subclass of cObject, so by deriving from cContainer

           you are also deriving from cObject.)

    </li>

    <li>

           Make sure the <bold>Automatically create as an object</bold> button

           is checked. (Note: If you are going to have more than one container

           of the same type, you may want to make a cContainerBackpack class, and then

           derive each individual backpack that. This tutorial assumes

           you only have one backpack though.)

    </li>

    <li>

           Underneath the checkbox, <bold>select the room or object</bold> that

           the container will be in.

    </li>

    <li>

           In the object's <bold>properties</bold> tab, you'll find a

           number of properties that were automatically added when

           you selected the "cContainer" class. You'll need to fill these in.

    </li>

    <li>

           Set the <bold>pVisual</bold> property, of course.

    </li>

    <li>

           <bold>pNLPParseName</bold> and <bold>pNLPNounName</bold> will

           need to be filled in. See <bold>How command parsing

           works</bold> and <bold>Noun cases and noun-verb agreement</bold>.

    </li>

   

    <li>

           Fill <bold>pContainerVolumeMax</bold> with the maximum volume

           that the container can hold, in liters.

           The <bold>pContainerVolumeScale</bold> should be filled in with

           a number from 0 to 1, indicating how much larger the container

           gets when you shove more objects in it. For example: A canvas

           bag will use 1.0, since the volume of the bag is dependent upon

           its contents. A chest would be 0.0 since shoving more stuff

           into a chest does <bold>not</bold> increase its volume.

    </li>

    <li>

           Fill <bold>pContainerWeightMax</bold> with the maximum weight

           the container can hold; a canvas bag cannot hold as much

           weight as a chest. You can add

           the <bold>pContainerWeightScale</bold> property to turn

           the container into a magic item; the default scale is 1.0,

           which means that if the container holds 10 kg of weight, it's

           10 kg heavier. However, if the scale is reduced, to 0.5, then

           the 10 kg of contents will only add 5 kg to the weight

           of the container.

    </li>

    <li>

           Set <bold>pVolumeSelf</bold> and <bold>pWeightSelf</bold> for

           the volume and weight of the container, without contents.

    </li>

    <li>

           If you wish your container to be openable,

           set <bold>pOpen</bold> to <bold>FALSE</bold>, causing the

           container to initially be closed. It can be set

           to <bold>TRUE</bold> if you wish it to start out opened.

    </li>

</ol>

 

<p>

    That's it. You now have a container that you can put stuff in.

</p>

 

 

 

<br/><section><p><bold><big>Locked containers</big></bold></p></section>

 

<p>

    To create a locked container:

</p>

 

<ol>

    <li>

           In the container's property tab, add the <bold>pLock</bold> property.

           Set it to <bold>TRUE</bold>. This defaults to the container being

           locked... which makes sense from a game point-of-view.

    </li>

    <li>

           Also add the <bold>pLockShape</bold> tab, and type in

           a string the identifies the shape of key that will

           open the container, such as <bold>"SkeletonKey12"</bold>. Some

           keys may fit to several shapes, allowing them to open

           several types of locks, just as master keys do. Conversely,

           you can have a container accept several key shapes by

           setting pLockShape to a list, like <bold>["SkeletonKey12",

           "SkeletonKey54"]</bold>.

    </li>

    <li>

           If you want your container to automatically lock itself after

           it has been unlocked, set <bold>pLockAutoLock</bold> to

           the number of seconds delay before it locks. This feature

           is extremely handy for multi-user interactive fiction,

           causing a container puzzle to reset. If you use automatic

           locking you may also wish to set <bold>pOpenAutoClose</bold> to

           have the container automatically close itself... a locked

           container does not good if it's left open.

    </li>

    <li>

           You'll need to create a key object that

           supports <bold>pKeyShape</bold> with <bold>"SkeletonKey12"</bold>.

           See the tutorial, <bold>cObject - Generic objects</bold> for

           information on general-purpose objects.

    </li>

</ol>

 

<p>

    That's it. Now, players (and NPCs) will have to unlock the

    container before accessing its contents.

</p>

 

 

 

<br/><section><p><bold><big>Other properties and methods</big></bold></p></section>

 

<p>

    Containers support some other properties and methods, such

    as ways to make the containers

    see-through, using <bold>pCanSeeContents</bold>. For more information

    see the documentation.

</p>

08 Talking with the client

Discusses how communication between the server and client works.

 

<section><p><bold><big>Talking with the client</big></bold></p></section>

 

<p>

    The Circumreality server application communicates with the client software

    (on the player's computer) over the internet. All communications

    are done using a MML string (basically XML).

    The Basic IF library supplies several functions useful for

    sending messages over the Internet to the client.

</p>

 

<p>

    Most of the functions have two flavors, one that sends the

    message immediately, although it will be queued up on the

    client. The other returns a MML string; several of these

    strings can be appended and then sent all at once to the

    client. Functions that return the MML string are generally

    prepended with "MML", such as MMLAudioPlay(), which returns

    a MML string, while MMLAudioPlay() sends the string immediately.

</p>

 

 

 

<br/><section><p><bold><big>Basic functions</big></bold></p></section>

 

<p>

    The most commonly used communication functions are:

</p>

 

<ul>

    <li>

           <bold>ActorLanguageSet()</bold> - Calling this function

           sets the current language (through LanguageSet()), to whatever

           language the actor has chosen as their preferred language.

           Before sending any message to an actor, you should call

           ActorLanguageSet() to ensure they will receive the message

           in the proper language.

    </li>

    <li>

           <bold>AudioPlay()</bold> - Plays a wave or MIDI (music) file

           on the user's computer.

    </li>

    <li>

           <bold>AutoCommand()</bold> - Has the user's computer send

           a command back to the server, such as "go north". At first

           glance this function may seem pointless, but it is useful

           since the message will be sent <bold>only</bold> when the

           client's playback queue  gets to this point.

    </li>

    <p>

           AutoCommand() is particularly useful for story segments

           since a small portion of audio and animation can be queued

           up on the user's system. When all that is played, the

           AutoCommand() will cause a message to be send back to the

           server, which in turn sends a bit more of the story.

    </p>

    <li>

           <bold>ObjectDisplayUpdate()</bold> - Whenever an object

           is changed (such as turned on or opened) its visuals may

           change. Calling ObjectDisplayUpdate() will update the

           object's visuals to one of the players.

    </li>

    <li>

           <bold>ObjectDisplayUpdateAll()</bold> - This is just

           like ObjectDisplayUpdate() except that all of the players

           in the room will get the visual update.

    </li>

    <li>

           <bold>RoomDisplayUpdate()</bold> - When the contents of

           a room has changed, calling this will update the visuals

           (room display, contents, inventory) for one of the

           players.

    </li>

    <li>

           <bold>RoomDisplayUpdateAll()</bold> - Just like

           RoomDisplayUpdate() except all players in the room are

           notified.

    </li>

    <li>

           <bold>Silence()</bold> - Calling this will add silence to

           the queue, creating a pause between actions.

    </li>

    <li>

           <bold>SpeakNarrator()</bold> - Causes the text-to-speech

           to be played on the player's computer. The voice used

           will be the narrator's.

    </li>

    <li>

           <bold>SpeakObject()</bold> - Causes the text-to-speech

           to be played on the player's computer. The voice used

           will be the object's.

    </li>

    <li>

           <bold>StringCleanup()</bold> - This utility function

           removes potentially unsafe characters from names (or other strings)

           that come from from the user's command.

    </li>

</ul>

 

 

<p>

    Some less commonly used functions are:

</p>

 

<ul>

    <li>

           <bold>AmbientLoopVar()</bold> - Some ambient sounds that

           include loops have the ability to loop in different ways

           depending upon the value of "ambient loop variables". This

           function lets the server set the value of the variable.

    </li>

    <li>

           <bold>AmbientUpdate()</bold> - This updates the ambient

           sounds played on a player's computer. The ambient sounds

           are a set of sounds based on the player's room, and sometimes

           even items carried by the player.

    </li>

    <li>

           <bold>ChatDisplayUpdate()</bold> - Update's the actor's

           chat window.

    </li>

    <li>

           <bold>ChatVerbUpdate()</bold> - Update's the actor's

           chat window.

    </li>

    <li>

           <bold>CommandLineShow()</bold> - Shows or hides the command

           line. If the command line is hidden the player must click

           on hotspots or menus, and cannot type in commands directly.

    </li>

    <li>

           <bold>Position360()</bold> - Changes the player's facing

           for the 360 degree images.

    </li>

    <li>

           <bold>VerbWindowShow()</bold> - Shows or hides the player's

           verb window.

    </li>

</ul>

 

 

 

<br/><section><p><bold><big>MML-string functions</big></bold></p></section>

 

<p>

    The MML string functions don't actually send a message to

    the player's computer. Instead, they return a string with the

    message. The message can then be sent at a later date, combined

    with other messages, or passed into other MMLxxx() functions.

</p>

 

<ul>

    <li>

           <bold>MMLAmbientLoopVar()</bold> - Like AmbientLoopVar()

           except it returns the string.

    </li>

    <li>

           <bold>MMLAmbientSounds()</bold> - This function combines

           several "&lt;Ambient&gt;" resources into a single "&lt;AmbientSounds&gt;..&lt;/AmbientSounds&gt;"

           message that can be sent to the client.

    </li>

    <li>

           <bold>MMLAudioPlay()</bold> - Like AudioPlay() except

           it returns the string.

    </li>

    <li>

           <bold>MMLAutoCommand()</bold> - Like AutoCommand() except

           it returns the string.

    </li>

    <li>

           <bold>MMLCommandLineShow()</bold> - Like CommandLineShow() except

           it returns the string.

    </li>

    <li>

           <bold>MMLDelay()</bold> - See the "Queues and delays" section

           for a complete description.

    </li>

    <li>

           <bold>MMLDisplayWindow()</bold> - Calling this will create

           a new window on the client's computer and display the provided

           object within the window. If the window already exists then

           it will be updated.

    </li>

    <li>

           <bold>MMLDisplayWindowMain()</bold> - This function combined

           MMLObjectDisplay() with MMLDislpayWindowMain() to make

           it very easy to update the image shown in the main window.

    </li>

    <li>

           <bold>MMLGroup()</bold> - MMLGroup() is used to wrap up groups

           of related object images (See MMLObjectDisplay() or the

           method VisualAndMenuGet()). The groups of images are then combined

           using MMLIconWindow().

    </li>

    <li>

           <bold>MMLIconWindow()</bold> - Creates a new window on the

           player's computer that displays a series of objects. If the

           icon window already exists then it will be updated with

           the new information. The objects

           are arranged into groups. For example: The inventory window

           has a group of items immediately held by the player character,

           followed by groups for items contained within other items.

    </li>

    <li>

           <bold>MMLObjectDisplay()</bold> - The MMLObjectDisplay()

           function creates an "&lt;ObjectDisplay&gt;" MML command, which

           is used to name an object, provide a visual for it, and

           perhaps a menu. You may also wish to look at the

           VisualAndMenuGet() method documentation.

    </li>

    <p>

           The &lt;ObjectDisplay&gt; MML

           command can be used in a few different ways: The MML

           string can be passed to MMLGroup() or MMLDisplayWindow() to

           specify what objects are displayed in an icon or display window.

           Alternatively, the &lt;ObjectDisplay&gt; MML can be sent directly

           to the client using ConnectionSend(); doing this will cause

           any visuals for the object to be updated to whatever is

           in the new MML.

    </p>

    <li>

           <bold>MMLObjectDisplayGroup()</bold> - This function

           is used to get the &lt;ObjectDisplay&gt; MML for the contents

           of an object.

    </li>

    <li>

           <bold>MMLPosition360()</bold> - Like Position360() except

           this returns the MML.

    </li>

    <li>

           <bold>MMLQueue()</bold> - See "Queues and delays" for more

           information.

    </li>

    <li>

           <bold>MMLQueueSimultaneous()</bold> - See "Queues and delays"

           for more information.

    </li>

    <li>

           <bold>MMLSilence()</bold> - Like Silence() except this returns

           the MML.

    </li>

    <li>

           <bold>MMLSpeakNarrator()</bold> - Like SpeakNarrator() except

           this returns the MML string.

    </li>

    <li>

           <bold>MMLSpeakObject()</bold> - Like SpeakObject() except

           this returns the MML string.

    </li>

</ul>

 

 

 

<br/><section><p><bold><big>Queues and Delays</big></bold></p></section>

 

 

<p>

    Whenever a message is sent to the client (using ConnectionSend()),

    there is an option for placing the message in the main queue

    or acting on it right away. If the message is placed in the main

    queue it will ignored until that point of the queue is reached.

</p>

 

<p>

    The queue allows you to have the client play a sound (waiting

    until the sound is finished), speak (waiting until the speech

    is finished), display an image, speak again (waiting until

    the speech is finished), etc.

</p>

 

<p>

    If the message are marked for immediate use, then all of the

    events will happen at the sime time... <bold>except</bold> the

    speech. <bold>The text-to-speech system can only generate one voice

    at a time</bold>, so even if you tell it to speak several sentences

    at once, it will end up speaking them one at a time.

</p>

 

<p>

    All of this can easily be done using ConnectionSend() and the

    MMLxxx() functions above.

</p>

 

<p>

    However, what if you want a sound to play concurrently with

    the second audio (both being played after the image is

    displayed)? Or what if you wanted the sound to play half

    way through the speech?

</p>

 

<p>

    To do this you would use <bold>MMLDelay()</bold>.

</p>

 

<p>

    The &lt;Delay&gt; MML command causes another queue to be created

    which runs at the same time as the original one. Therefore,

    if you call add MMLDelay (0, rWaveToPlay) into the queue then

    at the point in the queue where the message is processed, it

    will create a new queue that playes "rWaveToPlay". When

    rWaveToPlay is finished playing the second queue will

    automatically shut down.

</p>

 

<p>

    If you pass a time into delay, such as MMLDelay (5, rWaveToPlay)

    then the wave won't play until 5 seconds of the next sound (or

    speech) has been played. (For example: MMLDelay(5,rWaveToPlay) +

    MMLNarratorSpeak(...) will play the sound 5 seconds after the

    narrator begins to speak.)

</p>

 

<p>

    Since the amount of time to speak will vary depending upon the

    user's speed settings (or language selected), you can have the

    delayed wave play a percentage of the way through the next

    sound (or speech). Just use a negative number to indicate

    the percentage. (For example: MMLDelay(-50,rWAveToPlay) +

    MMLNarratorSpeak(...) will start the wave playing half way

    through.)

</p>

 

<p>

    But what if you want your secondary queue to play more than

    one wave, or perform some other action?

</p>

 

<p>

    That's where <bold>MMLQueue()</bold> comes in. It combines

    a series of MML actions into a queue MML. This queue can then

    be passed into MMLDelay(). After the delay occurs, the queue

    will be processed one at a time.

    (For example: MMLDelay(-50, MMLQueue(rWaveToPlay, rWaveOther)) +

    MMLNarratorSpeak(...) will play rWavePlay half way through the

    speech. When rWavePlay is finished, rWaveOther will be played,

    sequentially after rWavePlay, but concurrently with MMLNarratorSpeak()).

</p>

 

<p>

    You can also use MMLQueue() when calling ConnectionSend().

    If you have the message queued up in the main queue then nothing

    unusual happens. However, if ConnectionSend() is set to immediate

    playback, then the queued events (in MMLQueue()) will start playing

    in their own queue, but sequentially.

</p>

 

<p>

    The Basic IF Library also includes a

    function, <bold>MMLQueueSimultaneous</bold> that combines

    MMLDelay(0, xxx) with MMLQueue(). It just makes programming

    a bit easier.

</p>

 

<p>

    Using the MMLQueue() and MMLDelay() functions you can create

    complex timings on the user's computer. These are particularly

    useful for the story segments, which often rely on simple

    animations.

</p>

09 cStorySegment – Story segment objects

What story segments are and how to use them.

 

<section><p><bold><big>cStorySegment - Story segment objects</big></bold></p></section>

 

<p>

    Sometimes you will need to show players a "cut scene", which is

    basically a short animation (series of images) along with audio.

    For example: If the player meets an important NPC, you may wish

    to spend a minute having the NPC talk, showing the NPC interacting

    with the player, etc. Cut scenes are fairly common in game,

    although usually they're full motion video. (Circumreality doesn't allow

    for FMV.)

</p>

 

<p>

    You might even need to go one step futher, and have a cut

    scene with a series of options at the end, such as how the

    player can respond to the NPC's question. These responses

    might result in further cut scenes.

    Using this technique creates an experience similar to the

    traditional "Choose Your Own Adventure" books.

</p>

 

<p>

    Alternatively, you can string one cut scene after another,

    creating a linear story.

</p>

 

<p>

    All of these devices can be created using the cStorySegment

    class.

</p>

 

<p>

    The cStorySegment class allows you to display short (or long)

    animations (as a series of stills) and audio on the player's

    computer. When the animations have finished displaying, the

    story segment either moves onto the next story segment (creating

    a linear narrative) or displays a menu that lets the user

    chose what's next.

</p>

 

 

 

 

<br/><section><p><bold><big>Creating a new story segment</big></bold></p></section>

 

<p>

    Creating a story segment is easy:

</p>

 

<ol>

    <li>

           Just as described in the "cObject" tutorial, <bold>create

           a new object</bold>, but <bold>instead</bold> of deriving

           your room off cObject, derive it from <bold>cStorySegment</bold>.

           (cStorySegment is a subclass of cObject, so by deriving from cStorySegment

           you are also deriving from cObject.)

    </li>

    <li>

           Make sure the <bold>Automatically create as an object</bold> button

           is checked.

    </li>

    <li>

           Story segments <bold>should not be</bold> contained in

           other objects.

    </li>

    <li>

           In the object's <bold>properties</bold> tab, you'll find a

           number of properties that were automatically added when

           you selected the "cStorySegment" class. You'll need to fill these in.

    </li>

    <li>

           <bold>pNLPParseName</bold> and <bold>pNLPNounName</bold> can

           be filled in with the segment's name. See <bold>How command parsing

           works</bold> and <bold>Noun cases and noun-verb agreement</bold>.

    </li>

    <li>

           <bold>pAmbient</bold> can be filled in with a resource for

           the ambient sounds played during the story segment, or

           left blank.

    </li>

    <li>

           <bold>The menu</bold> properties need to be filled in; I

           will discuss them later..

    </li>

    <li>

           If the story segment's pStorySegmentHideUI is FALSE (which is

           the default), then you'll need

           to set <bold>pVisual</bold> to the final image of the story segment.

    </li>

    <li>

           Fill <bold>pStorySegmentCutScene</bold> in with the cut scene to

           be played for the story segment, along with <bold>pVisual</bold> with

           the final visual to be displayed to the user, giving them menu

           choices.

    </li>

    <p>

           If you want something more complicated, then you'll

           need to modify <bold>StorySegmentGet()</bold>. In

           the object's <bold>methods</bold> tab, you'll find an

           entry for the <bold>StorySegmentGet</bold> method. You

           will need to write this. See below.

    </p>

</ol>

 

 

 

<br/><section><p><bold><big>StorySegmentGet</big></bold></p></section>

 

<p>

    Each story segment should provide

    a <bold>pStorySegmentCutScene</bold> and <bold>pVisual</bold> property.

    However, if these are too limiting, then you

    may wish to write your own <bold>StorySegmentGet()</bold> method.

    This method is used to control what images are displayed on the

    user's computer, along with any sounds that are played.

</p>

 

<p>

    StorySegmentGet() returns a MML string that is passed directly

    to ConnectionSend(). What you place in the MML string is up

    to you, but (by convention) it should set the visuals for the

    main image using MMLDisplayWindowMain(). You may even

    wish to update the main display window several times.

</p>

 

<p>

    A typical StorySegmentGet() function will look something like this:

</p>

 

<p align=center><table width=80%>

    <tr><td><font face="courier new">

           return<br/>

           &tab;MMLDisplayWindowMain (...) +<br/>

           &tab;MMLSpeakNarrator (...) +<br/>

           &tab;MMLDisplayWindowMain (...) +<br/>

           &tab;MMLAudioPlay (...) +<br/>

           &tab;MMLDisplayWindowMain (...);

    </font></td></tr>

</table></p>

 

<p>

    Each MMLxxx() function is a step in the animation or audio.

    The "..." will depend upon the function, of course.

</p>

 

<p>

    For more information about generating MML strings,

    see the "Talking with the client" tutorial.

</p>

 

<p>

    If a StorySegmentGet() isn't written, the story segment will

    call MMLCutScene() and VisualGet(), and display the

    pStorySegmentCutScene and pVisual properties instead.

</p>

 

<br/><section><p><bold><big>The story segment's menu</big></bold></p></section>

 

<p>

    When the story segment is finished playing, it brings up a

    menu, allowing the user to choose an option. There are several

    ways of accomplishing this:

</p>

 

<p>

    The easiest is to fill in <bold>pStorySegmentOptions</bold> with

    a list of menu strings, such as ["Say hello to Fred", "Ask Fred

    about the sword", "Leave"]. You then need to

    fill in <bold>pStorySegmentActions</bold> with a list of story

    segment or room objects

    corresponding to the action taken. Example: [oStoryHelloToFred,

    oStoryAskFredSword, oRoomInTheCity]. If the object is a story

    segment then the new story segment will be run. If it's a room

    the the player's character will be moved into the room.

</p>

 

<p>

    If you do use the the <bold>pStorySegmentOptions</bold> property,

    you can also fill <bold>pStorySegmentMenuTimeOut</bold> to the

    number of seconds before the menu will time out. (If you leave

    the property blank, the menu won't time out.) When the menu

    times out, it will automatically chose the first item in the

    menu and do that.

</p>

 

<p>

    Alternatively, if you want more control over the menu, you

    can create a GeneralMenu resource and

    fill <bold>pStorySegmentMenu</bold> with the resource name.

    If you use a menu resource, <bold>pStorySegmentOptions</bold> will

    be ignored.

</p>

 

<p>

    You may still wish to fill in <bold>pStorySegmentActions</bold> though.

    The story segment object automatically handles user commands

    in the form of "Option X", where X is a number from 1+.

    If the menu item's command is "Option 1", then

    the player will be moved to pStorySegmentActions[0]. "Option 2"

    will move the player to pStorySegmentActions[1], etc.

</p>

 

<p>

    If your menu returns commands that are not "Option X", such

    as "Say hello to fred", then you will need to provide a

    NLPCommandParse() and NLPCommandParseQuery() message for

    the story segment, which is a lot more work.

</p>

 

<p>

    If you don't wish to display a menu at all, but wish to have

    the story segment automatically pass the player to another

    story segment or another room then fill

    in <bold>pStorySegmentAutoCommand</bold> with the command

    text to run when the segment is finished with its in animation.

    The easiest way to handle this is to set pStorySegmentAutoCommand

    to "Option 1" and have pStorySegmentMenuActions contain

    a list with one item, the next room or story segment.

</p>

 

<p>

    And if that weren't enough options, you can even have hot

    spots in the images displayed by the story segment's

    StorySegmentGet() call. If the user clicks on a hot spot,

    the specified command will be send. As with menu resources,

    using "Option X" will make programming much easier.

</p>

 

 

 

 

<br/><section><p><bold><big>Mini-choices</big></bold></p></section>

 

<p>

    Some story segments might include "mini choices" that (usually) reveal

    information that helps players make a choice for the story segment,

    but don't actually branch off into a new story segment.

</p>

 

<p>

    For example: A story segment might have the player encounter a door,

    and then decide to open the door or leave it alone. The player might

    wish some more information, and might be provided with

    the mini-choices of "Examine the door" or "Look through the keyhole".

</p>

 

<p>

    Selecting a mini-choice will speak some text (or play a cutscene),

    like "You look through a keyhole and see an orc waiting to

    ambush you."

</p>

 

<p>

    However, mini-choices have a cost. If they didn't, then players

    would aways select them. The costs might incur the risk of something

    else happening (branching to another story segment for a "wandering

    monster" instead of the keyhole description being played), or

    negatively affecting a NPC that the player is in conversation with,

    or perhaps the player is only allowed to choice "one of the above".

</p>

 

<p>

    To add mini choices:

</p>

 

<ol>

    <li>

           Add the choice description

           to <bold>pStorySegmentMenuOptions</bold> as usual.

    </li>

    <li>

           Under <bold>pStorySegmentMenuActions</bold>, use a sub-list

           for the action instead of a room.

    </li>

    <p>

           For example: In the "Look through the keyhole" option,

           the sub-list might be ["speak", "You look through a keyhole and see an orc waiting to

           ambush you."].

    </p>

    <li>

           Describe the consequence by

           filling in <bold>pStorySegmentMenuConsequences</bold>. If

           you <bold>don't</bold> fill the consequence in then the

           default is to use 1 action point, which effectively becomes

           a "Choose 1 of the following."

    </li>

    <p>

           NOTE: Once a mini-choice has been made, it cannot be made again if

           the player re-enters the story segment.

    </p>

</ol>

 

<p>

    The default consequences deal in "<bold>action points</bold>".

    The first time the player enters the story segment they

    are given a limited number of action points; Always less

    than the number of choices that require action points!

    A limited supply of action points forces the player to

    "Choose 1 of the following 2 choices", and turns

    the story segment into a small sub-game of its own.

</p>

 

<p>

    You can specify the number of action

    points in <bold>pStorySegmentActions</bold>. If you don't,

    then players will be given enough action points so

    they can only choose half of the available action-point-based mini-choices.

</p>

 

 

 

<br/><section><p><bold><big>Moving a player into a story segments</big></bold></p></section>

 

<p>

    Once the player is in a story segment, it's easy to for

    the player to move to other story segments or rooms, but

    how does the player get into a story segment in the first place?

</p>

 

<p>

    There are two ways:

</p>

 

<ol>

    <li>

           You can provide a story segment as an exit from a room,

           filling <bold>pExitXXX</bold> with the story segment

           object instead of a room object.

    </li>

    <li>

           Your code can call <bold>ActorMoveParty()</bold> to move the

           character into a story segment.

    </li>

</ol>

 

 

 

<br/><section><p><bold><big>Other properties and methods</big></bold></p></section>

 

<p>

    Unlike other classes, there really isn't much more to a

    story segment. cStorySegment is a fairly simple class. However,

    some more properties exist:

</p>

 

<ul>

    <li>

           <bold>pStorySegmentEnterAsParty</bold> - This flag defaults to

           TRUE, and forces all members of a party to enter (and leave) a story segment

           together. This ensures that party members don't just wander around,

           but that they don't wander into different paths of the story.

    </li>

    <li>

           <bold>pStorySegmentHideUI</bold> - Setting this flag in a story segment

           causes all the UI except for the main window to be hidden. This will prevent

           players from typing in commands, viewing inventory, or even

           talking to other players in the segment. Since most story segments

           are entered as a party and may want to talk with

           one another, pStorySegmentHideUI defaults to FALSE.

    </li>

    <p>

           However, if pStorySegmentHideUI is FALSE, then strangers in the story

           segment will also be able to see and talk to one another. This

           may break the immersion of the game. To solve this problem, put the

           story segments into their own "instance" (described later).

    </p>

    <li>

           <bold>pStorySegmentMenuExtraText</bold> - Provides an extra description

           to the command. If not specified, some "extra text"

           values are <bold>automatically generated for mini-choices</bold>.

    </li>

    <li>

           <bold>pStorySegmentIdentifySelf</bold> - Causes one or more NPCs

           to introduce themselves to the PCs.

    </li>

    <li>

           <bold>pStorySegmentKnowledgeAdd</bold> - Automatically calls KnowledgeAdd()

           for one or more pieces of knowledge.

    </li>

    <li>

           <bold>pStorySegmentQuestsAdd</bold> - Adds a quest to the PC's list.

    </li>

    <li>

           <bold>pStorySegmentMystery</bold> - Adds a mystery to the player's list

           by calling MysteriesAdd().

    </li>

</ul>

 

10 cStoryline – Storylines

Information about what storylines are and how to create them.

 

<section><p><bold><big>cStoryline - Storylines</big></bold></p></section>

 

<p>

    Many MUDs and MMORPGs let players choose a character from a

    good race or an evil race. Depending upon the goodness or

    evilness, their character begins in a different location

    in the virtual world.

</p>

 

<p>

    This is an example of a storyline; the player can choose

    the good storyline or the evil storyline. The MMORPG, Dark

    Age of Camelot, includes three storylines, one for each

    of three realms.

</p>

 

<p>

    An interactive fiction title could use storylines in

    different ways. Perhaps new players must first play a specific

    race. Once they have completed all the quests for that race, they're

    allowed a larger selection of races. This would create

    a world with two storylines.

</p>

 

<p>

    The basic IF library comes with a placeholder storyline

    object called <bold>oStorylineGeneric</bold>. If you are just

    playing around, or only have one storyline, then you can

    just use this object.

</p>

 

 

<br/><section><p><bold><big>Creating a storyline</big></bold></p></section>

 

<p>

    To create a storyline:

</p>

 

<ol>

    <li>

           <bold>Create an object</bold>, and base it off of <bold>cStoryline</bold>.

    </li>

    <li>

           Set the object's <bold>pNLPNounName and pNLPParseName</bold> to the

           storyline's name, such as "The good side". This name will

           be displayed as once of the storyline choices when a character

           is created. (If the world only provides one storyline then the

           player won't be given a choice.)

    </li>

    <li>

           Fill in <bold>pExamingeGeneral</bold> with a description about

           the storyline, such as "Play an evil character and fight the

           treacherous forces of good."

    </li>

    <li>

           <bold>pStorylineRaces</bold> should be filled in with a list

           of races that the user can choose from if they select the

           storyline. For example, the races in a good storyline might

           include humans, elves, dwarves, and halflings. The races in

           an evil storyline might be humans, orcs, and trolls.

    </li>

    <li>

           <bold>pStorylineRoomStart</bold> should be filled in with the

           room where new characters should first appear. If there

           is more than one room, then create a list of rooms.

    </li>

    <p>

           You can also provide a <bold>StorylineRoomStart()</bold> method

           and select the room based upon the character's race or gender.

           The default behaviour for StorylineRoomStart() is to return

           pStorylineRoomStart.

    </p>

    <li>

           If you wish your storyline to be off-limits to players until

           they have completed another storyline, or until they have

           paid, then write a <bold>StorylineQuery()</bold> method

           to check any properties assigned to the user. If the storyline

           can always be played then ignore the method.

    </li>

    <li>

           If you wish your storyline to provide the character with

           special equipment or skills then provide

           a <bold>StorylineSetup()</bold> method, which will be called

           after the character has been created.

    </li>

    <li>

           Finally, add the storyline object

           to <bold>gStorylines</bold> so the user creation code

           knows what storlines are available.

    </li>

    <p>

           gStorylines is accessed through the

           function, <bold>StorylinesEnum()</bold>, which begins with

           the list in gStorylines but pairs it down based upon

           SotyrlineQuery() calls.

    </p>

</ol>

 

 

11 cSaveToDatabase – Automatically saved objects

An easy way to save objects (such as NPCs) to a database.

 

<section><p><bold><big>cSaveToDatabase - Automatically saved objects</big></bold></p></section>

 

 

<p>

    If your interactive fiction title is designed to be offline,

    then the entire world setup (including all the objects

    in the world) will be saved when the user saves a game; you don't

    need to worry about this section.

</p>

 

<p>

    However, if you are using Circumreality to create a MUD or MMORPG, when

    a user quits, only their character information is saved.

    If the server is shut down (or crashes) then all NPCs will

    be deleted and started afresh the next time the server

    is rebooted.

</p>

 

<p>

    For a traditional MUD, this is no problem. However, NPCs in Circumreality

    can remember quite a lot about players, information that

    shouldn't be forgotten.

</p>

 

<p>

    To make an object that automatically saves itself to the database

    and then reloads when the IF title is restarted, just

    base it off of <bold>cSaveToDatabase</bold>. That's all you need

    to do.

</p>

 

<p>

    The cSaveToDatabase class keeps a log of all the objects

    belonging to it in gSaveToDatabase. When the IF title is

    shut down, all these objects (namely NPCs) are saved to

    the gDatabaseSaveTo ("SaveToDatabase") database. All the objects they

    hold are likewise saved; this way NPCs will continue to hold

    equipment.

</p>

 

<p>

    Just to be paranoid, oDatabase sets up a timer to trickle-save

    all the cSaveToDatabase objects. That way, if there is a crash,

    most of them will have been saved recently.

</p>

 

<p>

    When the IF title starts up, oDatabase sets a timer to occur

    in 0-seconds that loads in all the objects that were saved

    in gSaveToDatabse. Thus, all NPCs and their equipment will

    be reloaded.

</p>

 

<p>

    The reload process involves some gotchas that you should

    be aware of:

</p>

 

<ul>

    <li>

           If an object saved by cSaveToDatabase is also <bold>based on

           class X, but class X is deleted or renamed</bold>, then the object will

           fail to load.

    </li>

    <li>

           If a NPC/object based on cSaveToDatabase holds item Y when

           it is saved, when the NPC/object is reloaded, <bold>it will automatically

           move item Y into its possession.</bold> This might cause some

           confusion if you thought you modified your code to place item Y

           someplace else, but it keeps popping up in the NPC's hands.

    </li>

    <li>

           <bold>If a player kills a NPC that's defined in your code, such

           as oNPCFrank, then the next time you reboot oNPCFrank will

           be recreated.</bold> This happens because when oNPCFrank is killed,

           he is deleted from the oDatabaseSaveTo database. The next time

           the IF title starts, it doesn't find an entry for oNPCFrank

           in the database,

           so it starts with a fresh one.

    </li>

    <li>

           If you <bold>delete the "SaveToDatabase.mdb" file,</bold> all the information

           saved by cSaveToDatabase will be forgotten and you can start

           afresh.

    </li>

</ul>

12 Automatically suspended timers

An optimization that automatically suspends timers.

 

<section><p><bold><big>Automatically suspended timers</big></bold></p></section>

 

 

<p>

    In order to minimize CPU usage, timers for rooms and NPCs are <bold>automatically

    suspended</bold> when players aren't in them.

</p>

 

<p>

    The oDatabase object does the following once every second:

</p>

 

<ol>

    <li>

           <bold>Un-suspend all the timers</bold> in one

           randomly selected room, calling room.RoomSleep (FALSE).

           This will cause one room to wake up, even if there aren't any

           players in it, allowing NPCs to act (albiet slowly) even when players

           aren't in the room.

    </li>

    <li>

           <bold>Suspend all the timers</bold> in two randomly select rooms

           using room.RoomSleep(TRUE).

           If players are in the room, timers are <bold>not</bold> suspended.

           These suspensions ensure that if a player walks out of a room the room

           is eventually suspended.

    </li>

</ol>

 

 

<p>

    Plus, whenever <bold>a player or NPC</bold> walks into a room, the

    room ands its adjacent rooms are <bold>un-suspended</bold> so that NPCs

    become active.

</p>

 

<p>

    If you want a room whose timers don't get suspended then override

    RoomSleep() for the room.

</p>

13 Instances

Created instanced spaces in your world.

 

<section><p><bold><big>Instances</big></bold></p></section>

 

 

<p>

    Many multiplayer virtual worlds have "instances" that are private

    regions of the world that only the player and his friends can

    enter. For example: A player's house might be an instance. Or,

    a dungeon that's guaranteed private will have an instance.

</p>

 

<p>

    In MIFL, you can <bold>instance a map</bold> and all the rooms

    it contains, along with the contents of the rooms. If you wish

    a region or zone to be instanced, then just ensure that all the

    maps within the region or zone are individually instanced.

    You <bold>cannot</bold> instance individual rooms though,

    unless you put them into their own map.

</p>

 

<p>

    Making an instance is easy:

</p>

 

<ol>

    <li>

           <bold>Create</bold> an oMapMyInstance object based off of cMap,

           just like you would create any map object.

    </li>

    <li>

           Set <bold>pMapInstanced</bold> to TRUE.

    </li>

    <li>

           You may wish an instance to be deleted after it hasn't been used

           for a few days. To do so,

           set <bold>pMapInstancedExpires</bold>.

    </li>

    <li>

           Normally, if a player leaves an item in an instance, <bold>the

           item will be deleted when the instance saves</bold>. This ensures

           that players can't use instances to store equipment, instead of

           storing equipment in the bank. However, if you wish players

           to be able to store equipment in an instance, then

           set <bold>pMapInstanceSaveAll</bold> to TRUE.

    </li>

</ol>

 

 

 

<br/><section><p><bold><big>Some important points</big></bold></p></section>

 

<p>

    When you mark a map as pMapInstanced, the map's rooms and all the objects

    in the room are treated like a "template". When an instance of the

    map are created, all the rooms and objects of the template are cloned.

    Therefore, <bold>never</bold> allow player characters or non-template NPCs

    to move into the template. This shouldn't be difficult since all the methods

    and functions test for the instance and prevent movement into it.

</p>

 

<p>

    NPCs that are checked out of the database (those

    based on cSaveToDatabase) <bold>can't move into an instance</bold>. However,

    NPCs not in the database can move freely between instances. This ensures that

    a major NPC doesn't follow a player into an instance, and then the player shuts

    down the instance, permenantly trapping the NPC.

</p>

 

<p>

    Unless players form up into a party, they will go their separate ways

    when they enter an instance, even if they're following one another. If

    they form a party, they enter the leader's instance.

</p>

 

<p>

    NPCs that are intentionally chasing players will be to enter the player's

    instance.

</p>

 

<br/><section><p><bold><big>Behind the scenes</big></bold></p></section>

 

<p>

    Here's how instancing works behind the scenes:

</p>

 

<ol>

    <li>

           Whenever a player character (or NPC) moves from room to

           room, <bold>InstanceRoomRedirect()</bold> is called to see if the

           player has entered a new instance. If the PC or NPC doesn't

           switch instances then InstanceRoomRedirect() doesn't make any

           changes to the movement.

    </li>

    <li>

           If a PC or NPC <bold>enters a room with a different

           instance,</bold> InstanceRoomRedirect() determines

           if the instance has been loaded already. If it has, it just

           remaps the new room to the instanced room.

    </li>

    <li>

           If the instance hasn't been loaded, InstanceRoomRedirect() <bold>attempts

           to load a saved instance.</bold> If that succedes, the saved instance

           is used.

    </li>

    <li>

           If a saved instance cannot be found, then

           InstanceRoomRedirect() <bold>clones the template rooms and

           objects</bold> in the map and uses those.

    </li>

    <li>

           Once every 15 seconds (approximately), <bold>a timer tries to shut down one

           of the running instances.</bold> If any players are in the instance,

           the shutdown fails right away. If no players are there, the instance

           is saved to disk, and all the instance rooms and objects are deleted.

    </li>

    <li>

           The same background timer also <bold>deletes saved instanced that

           haven't been used for awhile</bold> (as specificed by pMapInstancedExpires).

    </li>

</ol>

 

14 Fractures

Information about fractured reality, a mechanism that lets players change a multiplayer world.

 

<section><p><bold><big>Fractures</big></bold></p></section>

 

 

<p>

    One of the "really big" problems of a multiplayer world is that

    player's can't change the world in any meaninful way because

    any changes would also be seen by other players.

</p>

 

<p>

    For example: If a village is under attack by goblins, and the

    player kills all the goblins, then the next time the player enters

    the village, it should be a happy place. However, if the player

    fails to kill the goblins, the village should remain a burnt-out

    ghost-town for the rest of the game. Thus, there are three

    versions of the village: During the goblin attack, successfully

    defended, and burnt.

</p>

 

<p>

    Once solution to this problem is to create a private instance for the

    village. However, the village will only ever be private, and

    players have zero chance of encountering other players when they enter.

</p>

 

<p>

    "Fractured reality" provides another solution. Basically, it allows three

    versions of the village to co-exist in reality. When the player enters

    the village, the player's character is whisked to one of the three

    versions of reality depending upon "fracture flags" set in the

    character. The same goes for all players, so that it's possible and likely

    that players will meet other players in whatever version of the village

    they're routed to.

</p>

 

 

 

<br/><section><p><bold><big>Fracturing rooms</big></bold></p></section>

 

<p>

    The first step to creating a fractured reality is to fracture some rooms.

    You can either fracture all the rooms in the map that need fracturing,

    or you can fracture the entrace/exit rooms to the map and have each

    fracture redirect the player character to another map.

</p>

 

<p>

    For each fracture, you need to:

</p>

 

<ol>

    <li>

           <bold>Create a standard room</bold> as you normally would. Since the

           rooms are variations on the same room, you'll probably make several

           copies of the original room, or base the fractured room objects off

           the original room.

    </li>

    <li>

           Come up with a lower-case string that will <bold>name</bold> the

           fracture, such as "firevillage" or "happyvillage". The default (original)

           version (which is being attacked by goblins) won't need a name.

    </li>

    <p>

           Several rooms can share the same fracture string; there's no need

           to have a "fireinn" and "happyinn" as separate from the village so

           long as it's in the same village.

    </p>

    <li>

           For each room, set <bold>pRoomFracture</bold> to list all the fractured

           versions of the room, as well as what fracture string determines what

           room the character is sent to.

    </li>

    <li>

           If you have an especially tricky situation, you

           may whish to override the <bold>RoomFracture()</bold> method

           for the rooms.

    </li>

</ol>

 

 

 

 

<br/><section><p><bold><big>Assigning characters to fractures</big></bold></p></section>

 

<p>

    Once the player has defeated or lost to the goblins,

    call <bold>cCharacter.FractureAdd()</bold>, passing in either the

    "firevillage" or "happyvillage" string. You'll

    need to also <bold>move</bold> the character (and anyone in his party)

    to the new fracture. If you don't move the character, the changes won't

    occur until after the character moves into another fractured room.

</p>

 

<p>

    If the character is a member of a party, make sure to call

    FractureAdd() for the <bold>leader</bold> of the party, since all

    party members take their fracture flags from their party leader.

</p>

 

<p>

    You can remove fracture flags by calling <bold>FractureRemove()</bold>.

    Calls to <bold>FractureQuery()</bold> will test to see if a flag

    has been set.

</p>

 

<p>

    An easy way to test whether an object exists within the player's

    fractured world is to call <bold>IsInDifferentFracture()</bold>.

</p>

 

 

 

<br/><section><p><bold><big>Objects and NPCs that only exist in a fracture</big></bold></p></section>

 

<p>

    You may run into cases in a multiplayer game where a NPC needs to disappear in the eyes

    of some players, but not others. For example: The player may complete a quest, one consequence

    of which is that the NPC disappears.

</p>

 

<p>

    To do this:

</p>

 

<ol>

    <li>

           Set oNPCThatDisappears.<bold>pDoesntExistForPlayer</bold> to TRUE. (You can set it

           to a lower-case fracture string, but TRUE is usually easier since the fracture string

           is automatically generated.)

    </li>

    <li>

           When the quest is completed and the character should disappear,

           display any relevent descriptions, and call oNPCThatDisappears.<bold>DoesntExistForPlayerSet</bold>().

    </li>

    <li>

           To see if the NPC doesn't exist for a specific player,

           call oNPCThatDisappears.<bold>DoesntExistForPlayer</bold>. Support for DoesntExistForPlayer() is

           automatic in the game's code, and is handled in other methods such

           as IsInvisible().

    </li>

</ol>

 

<br/><section><p><bold><big>Killable NPCs in a multiplayer world</big></bold></p></section>

 

<p>

    Traditional multiplayer worlds have a problem: Players aren't allowed to kill NPCs, because if they were,

    other players would never get to meet the NPCs.

</p>

 

<p>

    CircumReality has a solution for this. Players <bold>can kill NPCs</bold>. The NPCs will be

    resurrected <bold>but</bold> after the NPCs are resurrected, they won't be visible

    to the players that killed them... thanks to fractures.

</p>

 

<p>

    To make a killable NPC:

</p>

 

<ol>

    <li>

           Make sure the NPC is based off of the class, <bold>cSaveToDatabase</bold>. Almost all

           NPCs will be based off this anyway, since the class is necessary for them to remember

           players.

    </li>

    <li>

           Set the NPC's <bold>pDamageCanBeAttacked</bold> to TRUE, so the NPC can be attacked.

    </li>

    <li>

           Set the NPC's <bold>pDoesntExistForPlayer</bold> to TRUE, so that if can be fractured.

    </li>

    <li>

           <bold>Don't start the NPC with any directly created possessions of its own</bold>. There

           should be no oFredsShield and oFredsSword for the character oFred (who can be killed). If

           you do this, then only one player will be able to pull the object off the dead NPCs body.

    </li>

    <li>

           Instead, fill in <bold>pLootEquip, pLootCreate, and pLootCreateOnDeath</bold> as

           per LootCreate(). Make Fred's sword and shield part of his pLootEquip. That way, every time

           Fred is killed, a new sword and shield will be created for him.

    </li>

</ol>

 

<p>

    Players will be able to kill the NPCs. Other players will witness the NPC being killed too.

    When the NPC dies, its body can be looted. When the body disappear, the NPC is resurrected

    in its starting room with new loot. Anything on its body before the NPC dies will disappear.

    The player that killed the NPC, along with the player's party, won't be able to see the NPC

    after that, thanks to DoesntExistForPlayerSet().

</p>

15 Portals

Exits that let characters move quickly and easily around the world.

 

<section><p><bold><big>Portals</big></bold></p></section>

 

 

<p>

    Virtual worlds often include "portals", which are exits that

    jump large distances, or allow players to bypass parts of the

    world that are so uninteresting they haven't even been designed.

</p>

 

<p>

    For example: A city could be constructed as a number of interesting

    districts, such as the shopping mall, city hall, and the park.

    The residential neighborhoods might be uninteresting. What players

    really want is a way to get around between the three districts, as

    well as a way to exit the city.

</p>

 

<p>

    For example: Everquest II has free water transport. At the end of

    the dock is a bell. If the player rings the bell, they have a choice

    of several destinations where they can be transported to. The

    destinations are all far away.

</p>

 

<p>

    In more technical terms, <bold>a portal is an exit that lets a

    character quickly travel to any (known) maps in the region</bold>.

</p>

 

 

<br/><section><p><bold><big>Making a portal exit</big></bold></p></section>

 

<p>

    To make a portal exit:

</p>

 

<ol>

    <li>

           Set <bold>pRoomPortal</bold> in the room where the exit occurs. Use

           a number from 0 through 11, indicating a direction numbers, such

           as 0 for north, 1 for north-east, etc.

    </li>

    <li>

           Since portal exits are probably entry points to, make sure

           to <bold>include the room in the map's pMapEntries</bold>.

           See below.

    </li>

</ol>

 

<p>

    Players (and NPCs) can use the portal to instantly move to any map

    with known entry points.

    See below.

</p>

 

 

 

<br/><section><p><bold><big>Entry points in maps</big></bold></p></section>

 

<p>

    To allow players to portal to a map:

</p>

 

<ol>

    <li>

           Set the map's <bold>pMapEntries</bold> to all the rooms in the map

           that can be entered. The rooms are usually the same as all the portal

           rooms in the map, but not necessarily.

    </li>

    <p>

           For example: If you have a cMapShoppingDistrict that's an east/west

           street, then you will probably have exit portals at both the east and west

           ends of the street. Likewise, pMapEntries will have two rooms,

           one at the west end of the street, and one at the east end. Players

           that "Travel to the shopping district" will automatically be

           sent to the <bold>nearest</bold> room, east or west, depending upon where

           they started from.

    </p>

    <li>

           You may not want all maps to be instantly accessible by new players.

           Many maps won't be accessible until the player character "learns" about

           its existence from NPCs. To make a map that's unaccessible to new

           players, include a factoid object in <bold>pMapEntries</bold>. When

           the player character hears the factoid from a NPC, it should be

           added to the player's knowledge list (KnowledgeAdd()). The next time

           the player visits a portal, he will be able to access the new map.

    </li>

    <p>

           For example: The city might also include "Mr. Feld's house". Since

           the player character doesn't know of Mr. Feld's existence when the

           begin play, "Mr. Feld's house" is never shown in any of the portals.

           However, once a NPC gives the player a quest to rob Mr. Feld's house,

           the player character's KnowledgeAdd() is called, and the map

           can now be accessed through the portal.

    </p>

</ol>

 

 

 

 

<br/><section><p><bold><big>Advanced</big></bold></p></section>

 

<p>

    Some advanced portal features are:

</p>

 

<ul>

    <li>

           <bold>MapEntries()</bold> is the method that returns the pMapEntries

           values. You might wish to write your own MapEntries() method for

           special-case portals.

    </li>

    <p>

           Example: You might want a one-off encounter to happen when a user

           does, "Travel to distant city". There might be some bandits on the

           road that are encountered once, but never again. To impliment this,

           you'd write your own MapEntries() method for oMapBanditEncounter. If

           the bandits have already been dealt with, this returns NULL.

           If they haven't, it would return [[oRoomBanditEncounter, oMapDistantCity,

           1.0]]. This would cause all travel to oMapDistantCity to be automatically

           redirected to oRoomBanditEncounter.

    </p>

</ul>

16 Guard NPCs – Blocking access to doors

How to prevent characters from going through doors.

 

<section><p><bold><big>Guard NPCs - Blocking access to doors</big></bold></p></section>

 

<p>

    Guards that prevent players from going through a specific door are a common

    feature of IF. Basically, the player isn't allowed to pass until they provide

    the right identification for the guard, speak a password (such as solving

    the Sphinx's riddle), etc.

</p>

 

<p>

    To make a guard object:

</p>

 

<ol>

    <li>

           Write a <bold>ActionBlock()</bold> method for the guard object that

           traps the "move" action and returns TRUE if the players try to

           go through the door before providing proof of identity.

           Make sure to test that the guard is conscious (pDamageUnconscious) and

           alive (pDamageDeath).

    </li>

</ol>

17 Personal NPCs

Personal NPCs that are loaded when the player logs on, and unloaded when the player leaves.

 

<section><p><bold><big>Personal NPCs</big></bold></p></section>

 

<p>

    Circumreality supports "personal NPCs". These are NPCs that are intended to

    either hang out with the character (such as pets or henchmen), or

    which are unique to each player character (such the the PC's family).

</p>

 

<p>

    On a more technical note, a personal NPC is a NPC that is loaded

    from the database only when the player character logs in, and which

    is saved to the database when the player character logs out.

    There are some exceptions to this, such as when the personal NPC

    would have to be loaded into a room within a PC's instance when the

    instance hasn't been created yet.

</p>

 

<p>

    To make a personal NPC:

</p>

 

<ol>

    <li>

           Using any method, such as "new cRaceElf", <bold>create a NPC</bold>.

    </li>

    <p>

           Note: <bold>You cannot turn a MIFL-defined object into a NPC.</bold> This means

           that if you cannot use the MIFL editor to create an object that

           has the "Automatically create as an object" checkbox ticked, and then

           turn it into a personal NPC.

    </p>

    <li>

           Call <bold>PersonalNPCMake()</bold> to turn the NPC into a personal NPC.

    </li>

</ol>

 

<p>

    That's it.

</p>

 

 

<br/><section><p><bold><big>Personal NPC details</big></bold></p></section>

 

<p>

    Once the object is a personal NPC, it obeys the following rules:

</p>

 

<ul>

    <li>

           When the player logs on, all personal NPCs <bold>not</bold> in instances

           will be loaded.

    </li>

    <li>

           When the player enters an instance, all personal NPCs <bold>located

           to the instance</bold> will be loaded at that time.

    </li>

    <p>

           This means that a personal NPC in an instance is in stasis until the

           instance is entered. If you want to have the NPC to continue to

           act even if the PC isn't in the instance, then create a "quest" based

           off cQuest. Assigned it to the PC. Either have the quest act as

           the personal NPC's "brains" while the pesonal NPC is in stasis,

           or (less ideally because its slow), keep the instance

           loaded even when the PC isn't there using InstanceLoad().

    </p>

    <li>

           When an <bold>instance is shut down</bold> and a personal NPC is in it,

           the personal NPC will be saved. It will reload in the instance the

           next time the player enters a copy of the instance.

    </li>

    <p>

           This has some weird side-effects. If PC A joined PC B's party, and followed

           PC B into an instance, then the instance would be assigned to PC B.

           If PC A had a personal NPC that followed him, the personal NPC would

           enter into PC B's instance too. If PC A left their personal NPC

           in PC B's copy of the instance, and then logged off, his personal NPC

           would disappear from PC B's instance. The next time PC A logs on and

           enters a copy of the instance, perhaps PC C's copy of the instance,

           the personal NPC will be loaded into PC C's copy, NOT PC B's.

           You can ensure that personal NPCs will only be loaded into the

           instance of their PC by setting <bold>pPersonalNPCOnlyPCInstances</bold>.

    </p>

    <li>

           When the player character logs off, all his personal NPCs will log off.

    </li>

    <li>

           If the personal NPC dies, and <bold>pPersonalNPCResurrect</bold> is

           TRUE, then the NPC will be resurrected in the PC's (not the NPC's)

           last save room, specified by pRoomResurrectionLocation.

    </li>

    <li>

           If the personal NPC has <bold>pPersonalNPCPartyAuto</bold> set then

           the NPC will automatically be included as a quasi-member of any

           party that the PC joins; this is handy for creating pets and henchmen.

           (This property is set when PersonalNPCMake() is called.)

    </li>

    <p>

           For this to work, you will still need to include a goal

           that <bold>has the personal NPC follow the PC around</bold>,

           such as <bold>oAIGoalMoveFollowPersonalPC</bold>.

    </p>

</ul>

 

 

 

<br/><section><p><bold><big>Advanced</big></bold></p></section>

 

<p>

    Some other functions, methods, and properties to know about:

</p>

 

<ul>

    <li>

           <bold>pPersonalNPCs</bold> is a property in the player character that

           is used to store the PC's list of NPCs.

    </li>

    <li>

           Every personal NPC has <bold>pPersonalNPCFor</bold> set to the

           PC to whom they belong.

    </li>

    <li>

           <bold>pPersonalNPCPartyLast</bold> is the last time the personal NPC

           saw his PC.

    </li>

    <li>

           <bold>gPersonalNPCPartyAutoTime</bold> is the number of seconds that

           the NPC is allowed to be away from the PC before it is no longer

           automatically included in the PC's party.

    </li>

    <li>

           <bold>PersonalNPCDatabaseCheckOut()</bold> will load a personal

           NPC from the database so long as the room into which it should

           be loaded exists.

    </li>

    <li>

           <bold>PersonalNPCDelete()</bold> will dete a personal NPC.

    </li>

    <li>

           <bold>PersonalNPCPartyAuto()</bold> is a method to test and see if

           a personal NPC is in pPersonalNPCFor's party.

    </li>

</ul>

18 Replacing generic phrases

How to create more interesting writing.

 

<section><p><bold><big>Replacing generic phrases</big></bold></p></section>

 

<p>

    Circumreality uses a lot of <bold>generic phrases</bold>, like "%1 (pick/pick/picks) up %2", from

    sPerceiveMoveGet. These phrases get boring after awhile,

    particularly when something signficant has happened. For example:

    Instead of, "You pick up the jewel.", you might want to

    have the computer say, "Eyes filled with green, you eagerly pick up up the jewel."

</p>

 

<p>

    To do this:

</p>

 

<ol>

    <li>

           Create a <bold>StringReword()</bold> method for your oJewel object (which is

           picked up.)

    </li>

    <p>

           You could alternatively create a StringReword() for the room that the

           jewel appears in, or for the object upon which player characters are based.

           For this particular example, the jewel object works best.

    </p>

    <li>

           Write code for StringReword() that checks for sPerceiveMoveGet and the

           use of oJewel. If these match, it returns the new string. For example:

    </li>

    <blockquote><font face=courier>

           if (IsList(Replace2) &amp;&amp; (Replace2[0] == oJewel) &amp;&amp; (String == sPerceiveMoveGet))

           <br/>&tab;return "Eyes filled with green, %1 eagerly (pick/pick/picks) up %2.";

    </font></blockquote>

</ol>

 

 

<p/>

 

<p>

    StringReword() is called by <bold>NLPStringFormat2()</bold>, so it

    only works in cases where the string if being formatted

    by NLPStringFormat2().

</p>

 

 

 

<br/><section><p><bold><big>Other ways</big></bold></p></section>

 

<p>

    Circumreality also provides some other ways to reword generic phrases:

</p>

 

<ul>

    <li>

           <bold>StringActorEnterLeave()</bold> can be used to change "X enters from the north" or

           "X goes north"

           into something more exciting.

    </li>

</ul>

19 Custom commands for objects

Creating custom commands for objects.

 

<section><p><bold><big>Custom commands for objects</big></bold></p></section>

 

<p>

    There are several ways to create custom commands:

</p>

 

<ul>

    <li>

           Modify <bold>oParserVerb</bold> with your own commands. This works best when

           creating commands that will be supported by a number of objects (of different

           classes). See The "How command parsing works" tutorial for more information

           on this.

    </li>

    <li>

           In each object, write your

           own <bold>NLPRuleSetTempVerbs()</bold> and <bold>NLPCommandParse()</bold> methods.

           This is very flexible, but is a fair amount of work.

    </li>

    <li>

           The <bold>easiest</bold> way is to use the ObjectCommandXXX() methods,

           as described below.

    </li>

</ul>

 

 

 

<br/><section><p><bold><big>ObjectCommandXXX() methods</big></bold></p></section>

 

<p>

    The easiest way to add a custom command to an object is to

    provide the ObjectCommandXXX() methods for the object (or the object's

    class).

</p>

 

<p>

    For example: You might want a special command to "Turn over the table",

    perhaps because a secret message is written underneath.

</p>

 

<p>

    To do this:

</p>

 

<ol>

    <li>

           <bold>Come up with a lower-case string name</bold> for the action,

           like "turnover".

    </li>

    <li>

           Since the player must be able to access the bookshelf to turn it

           over, <bold>prefix you name with "access",</bold> turning it into "accessturnover".

           If the player only needed to see the object, then you'd

           use "seeturnover", or "holdturnover" if the player's character needed

           to hold the object.

    </li>

    <li>

           Write your own <bold>ObjectCommandEnum()</bold> method for the table.

           In this case, CommandList would merely have ["accessturnover",

           "Turn over &lt;object&gt;", "flip *1"] appended.

    </li>

    <p>

           "accessturnover" is the identifier for the command. "Turn over &lt;object&gt;" is

           the primary command that's parsed, as well as the command that's displayed

           in the object's context menu. "flip *1" is an alternate way of parsing

           the command; you could leave this out in most cases.

    </p>

    <li>

           Write you own <bold>ObjectCommandIsValid()</bold> method that returns

           TRUE if it's passed a CommandID of "accessturnover".

    </li>

    <li>

           Finally, write <bold>ObjectCommandAct()</bold> to actually flip the

           table. This code might refresh the room's image with an upside-down table

           and have the narrator speak something.

    </li>

</ol>

 

<p/>

 

<p>

    <bold>Design tip:</bold> Traditionally, interactive fiction titles have

    huge problems with "guess the verb", where the player must (a) guess that

    turning the table over will help solve a puzzle, and (b) guess the wording

    for "turning the table over". Players <bold>don't like</bold> guessing

    the verb! To be nice to the player, unless guessing the verb is very

    important, always make sure to include important actions

    in the object's context menu, by writing your own <bold>ContextMenuItemGet()</bold>.

    If you are using the ObjectCommandXXX() methods, then

    you <bold>don't have to</bold> write your own ContextMenuItemGet() because

    the commands will automatically be displayed.

</p>

20 Sending E-mail from an online IF file

Some information about sending E-mail that you should know.

 

<section><p><bold><big>Sending E-mail from an online IF title</big></bold></p></section>

 

<p>

    When Circumreality is used for online titles, it likes to send E-mail to players

    to notify them of:

</p>

 

<ul>

    <li>

           <bold>Send "activation passwords"</bold> to the E-mail accounts supplied by

           new users.

    </li>

    <li>

           Alert players that their <bold>characters have received game-mail.</bold>

    </li>

    <li>

           Warn players when their <bold>user accounts are about to be deleted</bold> because

           they haven't been used for awhile.

    </li>

</ul>

 

 

<p>

    For Circumreality to send E-mail, you need to fill in the following global variables:

</p>

 

<ul>

    <li>

           <bold>gEmailAdministrator</bold> - The administrator's E-mail account that is

           actually read, like "Fred@MSN.com". Occasional notifications may be E-mailed here.

    </li>

    <li>

           <bold>gEmailDomain</bold> - The domain name where your E-mails are

           being sent from. For example: If your automatic E-mails are sent

           from "AutoMail@mXac.com.au" then the domain name would be "mXac.com.au".

    </li>

    <li>

           <bold>gEmailSMTPServer</bold> - The SMTP server to send E-mails

           through. If you don't know what this is, then look in Outlook Express

           (or whatever E-mail you use), for your POP and SMTP server settings.

           The SMTP name is for outgoing mail messages, and might look like

           "mail.bigpond.com".

    </li>

    <li>

           <bold>gEmailMailSendEmail</bold> - This is the E-mail associated with

           your automatic E-mails, like "AutoMail@mXac.com.au".

    </li>

    <p>

           Despite the "Do not reply to this mail" warning, <bold>People

           will reply</bold> to automatic E-mails. You should occasionally check

           your automatic E-mail's in-box and empty the messages.

    </p>

    <li>

           <bold>gEmailAccessPasswordEmail</bold> - Like gEmailMailSendEmail,

           except this is used when an access password is mailed.

    </li>

    <li>

           <bold>gEmailServerName</bold> - Change this to the name of your

           world, like "Fred's world at www.Fred.com". It will be included in

           all automatic E-mails so players know which online IF title is

           alerting them.

    </li>

    <li>

           <bold>gEmailAuthUser and gEmailAuthPassword</bold> - Most E-mail

           servers require that you provide an authorization user account

           and password before they send E-mail. You may need to fill these

           out for E-mail to be successfully sent. (You can look at the logs

           to see if it was send successfully.)

    </li>

    <li>

           <bold>gUserAccessPassword</bold> - Set this to TRUE to

           require users to enter a valid E-mail address to access the online game.

    </li>

</ul>

 

 

<p>

    You might also wish to change these:

</p>

 

<ul>

    <li>

           <bold>gUserAccessPasswordValidTime</bold> - How long an access password will

           be valid before the player needs to reapply. This defaults to 30 minutes.

    </li>

    <li>

           <bold>sMailSendGameMailName</bold> - This is the friendly E-mail string

           displayed instead of gEmailMailSendMail. You might wish to set this

           to "MYWORLD automatic E-mailer", or something.

    </li>

</ul>

21 Rules of conduct and end-user license agreements

How to add rules of conduct or an end-user license agreement to your online game.

 

<section><p><bold><big>Rules of conduct and end-user licence agreements</big></bold></p></section>

 

<p>

    It's fairly common for online games (MUDs and MMORPGs) to require that players

    agree to a "Rules of conduct" or "End user license agreement (EULA)" before

    they are allowed to play.

</p>

 

<p>

    Circumreality automatically requires users to agree to rules of conduct before being

    allowed to play. You can change the rules of conduct to suit your needs.

</p>

 

<ul>

    <li>

           Change <bold>rLogonMainEULA</bold> to whatever rules of conduct you

           wish to require.

    </li>

    <li>

           <bold>Every time</bold> you change rLogonMainEULA, make sure

           to change <bold>gEULAVersion</bold> to a new number. When gEULAVersion

           is changed, all users (new and old) will be required to agree to

           the new rules of conduct.

    </li>

    <li>

           If you <bold>don't</bold> require users to agree to the rules,

           then set <bold>gEULAVersion</bold> to NULL.

    </li>

</ul>

 

 

22 Linking to other worlds

How to link to another world.

 

<section><p><bold><big>Linking to other worlds</big></bold></p></section>

 

<p>

    You may wish to provide exits in your world that link to other

    worlds. The character information won't be transmitted, but the

    player will quickly be forwarded onto the new world.

</p>

 

<p>

    To do this:

</p>

 

<ol>

    <li>

           <bold>Create a &lt;TitleInfo&gt;</bold> resource for the other world.

           When you create the resource, use the option in the resource

           editor to load all the information from an existing link

           file, such as MyWorld.crk.

    </li>

    <li>

           When the user goes in a specific direction, enters a portal,

           or presses a linking book, call <bold>LogOff()</bold> with the

           &lt;TitleInfo&gt; resource.

    </li>

</ol>

23 Where to store your CircumReality files

Some tips about where to store your CircumReality files on your hard drive.

 

<section><p><bold><big>Where to store your CircumReality files</big></bold></p></section>

 

<p>

    When you create a world using CircumReality, you'll probably end up creating

    hundreds of different files, including MIFL libraries, 3D scenes, sound

    files, etc.

</p>

 

<p>

    My recommendation for how to store them is:

</p>

 

<ol>

    <li>

           <bold>Create a directory</bold> on your hard drive, like "c:\WorldFiles".

    </li>

    <li>

           <bold>Add a link to the "Startup"</bold> folder underneath "Programs" in the Start menu.

           Have the link call "subst.exe w: c:\WorldFiles". What this will do

           is remap the w: drive to c:\WorldFiles. (You'll need to reboot to have this

           take effect, since the link is only run on start-up.)

    </li>

    <p>

           This is handy, because if you decide to move your world files to a different

           directory, you just change the "subst.exe" link and your

           file dependencies <bold>won't</bold> be broken.

    </p>

    <li>

           <bold>Within the w: drive, create sub-directories</bold> for:

    </li>

    <ul>

           <li>

                  <bold>Audio</bold> - Audio files are saved here.

           </li>

           <li>

                  <bold>Scenes</bold> - Save your "3D Outside the Box" scenes here.

           </li>

           <li>

                  <bold>Scripts</bold> - This is where you save for MIFL project file and

                  MIFL libraries.

           </li>

           <li>

                  <bold>Textures</bold> - Save texture bitmaps here.

           </li>

           <li>

                  <bold>Voices</bold> - Save your text-to-speech voices here.

           </li>

    </ul>

</ol>

 

<p/><section><p><bold><big>Text-speech voice locations</big></bold></p></section>

 

<p>

    The default libraries have several resources for the standard English

    text-to-speech voices, such as: rVoiceFemale1, rVoiceFemale2, rVoiceFemale3,

    rVoiceMale1, ..., rVoiceMale5, rVoiceNarrator, rVoiceMasterFemale1 ...

    rVoiceMasterFemale3, rVoiceMasterMale1 ... rVoiceMasterMale5.

</p>

 

<p>

    By default, these point to locations in "c:\program files\mxac\CircumReality".

</p>

 

<p>

    You may wish to override these in the following cases:

</p>

 

<ul>

    <li>

           If you only use a few of the standard voices, then override some of

           the extra voices to point back at a voice you are planning to

           use. Thus, if you're only using the first two female voices,

           then modify rVoiceFemale3 to one of the text-to-speech voices

           you used in rVoiceFemale1 or rVoiceFemale2. If you don't do

           this then when the .crf file is built, it will include the third

           female voice, making your .crf file larger than you need.

    </li>

    <li>

           If you want to provide your own custom text-to-speech voices.

    </li>

</ul>

29 Sample work involved in creating a CircumReality Map

A "brief" list of the work required to create a CircumReality map.

 

<section><p><bold><big>Sample work involved in creating a CircumReality map</big></bold></p></section>

 

<p>

    This is a list describing the work I undertook to create a CircumReality

    map. The map, of course, is just a portion of the entire game.

</p>

 

<ol>

    <li>

           Figure out roughly <bold>what happens</bold> in the map, why the players are there, etc.

    </li>

    <li>

           Figure out <bold>what NPCs exist</bold>, why they're important to the players,

           and how the NPCs interact with one another.

    </li>

    <li>

           Determine <bold>what the map should look like</bold>, what buildings there should be, etc.

    </li>

    <li>

           Using 3D Outside the Box, <bold>create the topography</bold> for the map.

    </li>

    <li>

           Using 3D Outside the Box, <bold>create shells for buildings</bold> and other structures or

           objects that affect room layout. These don't need to be detailed for now

           since the important part it determining where the rooms go.

    </li>

    <li>

           <bold>Create a cMap object.</bold>

    </li>

    <li>

           (Optional) Create a <bold>grid of room objects using oRoomXXxYY</bold>, so that the room locations and

           exits are automatically calculated.

    </li>

    <li>

           For rooms in the grid, fine-tune the location

           using <bold>pLocation</bold>, as well as automatic exits,

           by setting pExitXXX to NULL.

    </li>

    <li>

           <bold>Create non-automatic rooms</bold>, as per usual.

    </li>

    <li>

           <bold>Create doors.</bold>

    </li>

    <li>

           Revisit the room objects and assign them to special classes,

           such as for indoors, near the beach, etc.

    </li>

    <li>

           Make sure to set <bold>room flags</bold> like pProvidesLight and pRoomIsOutside.

    </li>

    <li>

           <bold>Create NPC objects</bold> based on cRaceXXX for each of your NPCs.

           Give them a pNameReal, pGender, and not much else.

    </li>

    <li>

           Make sure the NPCs are maked as <bold>cSaveToDatabase</bold> so

           they have a memory.

    </li>

    <li>

           You may want certain <bold>merchants</bold>, using cMerchant, or guards

           with pIsGuard.

    </li>

    <li>

           <bold>Can the NPCs be attacked?</bold> Fill in pDamageCanBeAttacked.

    </li>

    <li>

           Fill in the <bold>NPC's work and sleep schedules</bold> with

           pAIScheduleLocationEat, pAIScheduleLocationLive, pAIScheduleLocationWork,

           pAIScheduleTimeEat, pAIScheduleTimeSleep, pAIScheduleTimeWork.

           If a NPC's schedule is critical then set pDontSleep.

    </li>

    <li>

           Figure out <bold>what the NPCs do during the day</bold> and

           write AIScheduleEnum(), AIScheduleGoal(), AIScheduleLocatio(),

           and AIScheduleWhatDoing().

    </li>

    <li>

           You may need to <bold>write some of your own goals</bold> based off

           of cAIGoal.

    </li>

    <li>

           Fill in the <bold>NPCs' relationships</bold> with pAIRelationships.

    </li>

    <li>

           <bold>Create any objects</bold> that are important for the game,

           as well as providing an examine description, pExamineGeneral.

           Don't bother with custom artwork yet, leaving the pVisual blank

           for many objects.

    </li>

    <li>

           Likewise, create objects that the <bold>NPCs will carry</bold>.

    </li>

    <li>

           Create <bold>books that expose backstory</bold> to players, cBook.

    </li>

    <li>

           Some rooms will <bold>spawn</bold> hidden (or visible) objects.

           Fill in pSpawnTime, pSpawnClass, pSpawnResourceTime, and

           pSpawnResourceClass.

    </li>

    <li>

           <bold>Allow NPCs to be given or shown objects</bold> using AIListenForActCmdOffer()

           and AIListenForActCmdShow().

    </li>

    <li>

           <bold>Create conversations scripts</bold>, based on cConvScript, for NPCs

           to talk to one another as part of AIScheduleGoal(),

           or chance meetings, using pAIConvScripts.

    </li>

    <li>

           Some of the <bold>conversation scripts may reveal cKnowledge</bold> objects to

           players that listen in, so you'll need to create some knowledge

           objects at this point.

    </li>

    <li>

           Create <bold>cKnowledge objects that the player can ask</bold> NPCs, and

           then tell to other NPCs. Fill in pAIConvStories.

    </li>

    <li>

           Have the <bold>NPCs respond to specific knowledge</bold> by

           writing PerceiveKnowledgeSay().

    </li>

    <li>

           Have the <bold>NPC respond to other player actions</bold> by

           writing PerceiveXXX() methods. Some objects may prevent characters

           from leaving the room, using ActionBlock().

    </li>

    <li>

           Give <bold>NPCs skills</bold>, modifying pSkills.

    </li>

    <li>

           Fill in <bold>pDescribed for the NPCs, rooms, and objects</bold>.

    </li>

    <li>

           Fill in <bold>pExamineGeneral for NPCs, rooms, and objects</bold>.

    </li>

    <li>

           Fill in <bold>pAIInfoSpeakXXX</bold> for NPCs, such

           as pAIInfoSpeakFuturePlansNPC.

    </li>

    <li>

           <bold>Does the NPC like gifts, or can the NPC be bribed?</bold> Fill in

           pAIValueObject and pAIOfferCanBribe.

    </li>

    <li>

           <bold>Create any story segments</bold>, based on cStorySegment.

    </li>

    <li>

           <bold>What favors does the NPC grant once the NPC likes the PC?</bold> Changed pAIFavor.

    </li>

    <li>

           <bold>Is player-vs-player combat (PvP) allowed?</bold> Set pRoomCanAttack.

    </li>

    <li>

           Using 3D Outside the Box, and <bold>create models for your objects</bold>.

    </li>

    <li>

           Place these <bold>models in a resource, and assign a pVisual property</bold> to

           your objects.

    </li>

    <li>

           Add details to your 3D Outside the Box scene, such as furniture, knicknacks,

           and distant mountains (pMapMountains).

    </li>

    <li>

           Using an administator account, use the <bold>"change my appearance"

           and "change my voice"</bold> command

           to create visuals for each of the characters. Then type in "appearance info"

           to see the code for character's appearance voice, which

           you can then paste into pVisualModifiers and pVoiceSub.

    </li>

    <li>

           <bold>Record any sounds</bold> you might need, or find a sound library on

           the internet.

    </li>

    <li>

           <bold>Create ambient sound resources</bold> and place them in the world.

    </li>

    <li>

           <bold>Create a custom text-to-speech lexicon</bold> using

           the mXac NLP program included in 3D Outside the Box. Specify

           the pronunciations of mispronounced words, particularly names.

           Have your derviced

           text-to-speech voices use the custom lexicon.

    </li>

</ol>

30 “Shipping” your CircumReality title

Goes through the steps necessary to "ship" a Circumreality title and put it online.

 

<section><p><bold><big>"Shipping" your Circumreality title</big></bold></p></section>

 

 

<p>

    Once you have completed your Circumreality title, or

    if you just want to distribute it to some friends so they

    can try it out, you need to do the following:

</p>

 

<ol>

    <li>

           Create a <bold>TitleInfo</bold> resource.

    </li>

    <li>

           Generate and distribute your <bold>.crf or .crk file.</bold>

    </li>

    <li>

           (Optional) Put your <bold>server online.</bold>

    </li>

</ol>

 

 

 

 

<br/><section><p><bold><big>Create a TitleInfo resource</big></bold></p></section>

 

<p>

    The "TitleInfo" resource provides information about the

    interactive fiction title, such as whether it's run single-player

    or as an online-game, the end user license agreement, and

    a description of the title.

</p>

 

<p>

    Every interaction fiction <bold>must</bold> have one TitleInfo resource

    named <bold>rTitleInfo</bold>. The server library

    comes with a default TitleInfo named <bold>rTitleInfo</bold>.

    Therefore, if you will need to override it by creating your

    own TitleInfo resource called rTitleInfo.

</p>

 

<p>

    To do this:

</p>

 

<ol>

    <li>

           Select the <bold>Add new resource</bold> menu item from

           the <bold>Misc</bold> menu.

    </li>

    <li>

           In the "Add a resource" page, press the <bold>TitleInfo</bold> button.

    </li>

    <p>

           The title info "Modify resource" page will appear.

    </p>

    <li>

           Change the <bold>name</bold> to <bold>rTitleInfo</bold>.

    </li>

    <li>

           Press the <bold>Add</bold> button to add a resource for

           a given language. You can have different TitleInfo resources

           for each language, since you might wish to translate the

           product description and end-user license agreement.

    </li>

    <p>

           The "Title information" page will appear.

    </p>

    <li>

           Type in the product's <bold>name</bold>, <bold>short

           description</bold>, <bold>long description</bold>, and <bold>web site</bold> in the

           first tab.

    </li>

    <li>

           If the interactive fiction title can be run offline (without

           requiring an Internet connection) then <bold>check</bold> the

           item in the third tab allowing for this. Otherwise, uncheck

           it.

    </li>

    <li>

           If the interactive fiction title can be run over the Internet,

           visit the <bold>third tab.</bold> See below.

    </li>

    <li>

           Make sure to <bold>set the title's file name</bold> in the resource

           to the same name that you'll be using for the link file. For

           example, if you're distributing a "MyWorld.crk" link file,

           then enter "MyWorld" for the filename. This

           file name <bold>must be unique</bold> amongst all the worlds.

    </li>

</ol>

 

<p>

    For the interactive fiction title to run over the Internet you'll

    need to tell the editor what "shards" you'll have running.

    A shard is copy of the world. If you don't have many users you'll

    only have one shard. However, if you have thousands of users,

    one computer may not be fast enough to handle all those users,

    so you'll need run several servers, each one with a different

    shard. (You could even run several shards on one computer.)

    You can also use shards to provide subtly different experiences,

    with some shards encouraging player vs. player activities,

    while others encourage role playing. (See below.)

</p>

 

<p>

    To add a shard (in the "Internet" tab):

</p>

 

<ol>

    <li>

           Press the <bold>Add a new shard</bold> to add a new one.

    </li>

    <li>

           Type in a <bold>name</bold> and <bold>description</bold> for

           the shard.

    </li>

    <li>

           Type in the domain name of the <bold>server</bold>, such

           as <bold>MyIFServer.com</bold>.

    </li>

    <p>

           If your server doesn't

           have a domain name, but does have a fixed IP address

           then type in the IP address, such as <bold>12.34.567.89</bold>.

    </p>

    <p>

           If you don't know either the name or fixed IP address,

           the enter a website URL, <bold>http:://www.MyIFWebsite.com</bold> that

           can be updated with the IP address; with this last option,

           users will be asked for the IP address every time they run

           your IF title. (By the way, when you run your server, the

           main window will display the computer's IP address in

           the title.)

    </p>

    <p>

           If you have no clue about what I'm talking about then you'll

           need to find a server provider to host your IF title. They'll

           tell you all about domain names and IP addresses.

    </p>

    <li>

           Type in the <bold>port</bold> your shard will be listening

           to. If you don't know what this means then enter a number

           between 4000 and 5000. (The only reason why a port would matter

           is if another Internet application were using the same port

           (unlikely) or another IF title or MUD were using the same

           port (possible).)

    </li>

    <li>

           Type in a <bold>parameter</bold> so that your code can

           identify what shard it's running on. See below.

    </li>

</ol>

 

<p>

    Each shard allows a "parameter" to be typed in. When the

    shard is running, the software can access this parameter

    by calling the <bold>ShardParam()</bold> call.

    You can use the parameter to cause different shards to act

    differently.

</p>

 

<p>

    For example: You might want one of your

    shards to allow player-vs-player combat. If you set your

    shard parameter to "PvP" and then test (ShardParam() == "PvP")

    to see if player-vs-player is allowed.

</p>

 

<p>

    If the IF title is being run stand-alone, without any Internet

    connection, then the ShardParam() call will return "offline"

</p>

 

<p>

    If you wish to test your application with differnt ShardParam()

    values, you can set the shard to test with on the first page

    of the editor. When you first run CircumrealityWorldSim.exe (this application)

    to edit your Circumreality project (.mfp) there's an option at the bottom

    page for which "Shard number" you wish to use. Type in a

    number (1 and up) for the shard number; ShardParam() will return

    the value of that shard number. If you wish to test the offline

    behavior then enter "0" into the shard number.

</p>

 

 

<br/><section><p><bold><big>Generate and distribute your .crf or .crk file</big></bold></p></section>

 

<p>

    Once you have entered your rTitleInfo resource:

</p>

 

<ol>

    <li>

           Select the <bold>Compile</bold> menu item from

           the <bold>Misc.</bold> menu.

    </li>

    <li>

           Select the <bold>Test compiled code</bold> menu item from

           the <bold>Misc.</bold> menu.

    </li>

</ol>

 

<p>

    Running these menu options causes the editor to generate

    a .crf and .crk file for you. The files are located in the

    same directory as your main project file (.mfp). They have

    the same name as your project file, but have different

    extensions (with .crf or .crk on the end instead of .mfp).

</p>

 

<p>

    If you wish your players to run your interactive fiction

    offline you'll need to distribute the <bold>.crf</bold> file

    on your web site. This is a <bold>large</bold>, and can

    easily be hundreds of megabytes for a finished title.

    A very small title will be more like 30 MB.

</p>

 

<p>

    All your offline players need to do is install and

    run the Circumreality client. When they're asked what interactive fiction

    title to run they select the <bold>.crf</bold> file

    your have provided.

</p>

 

 

<br/><section><p><bold><big>Offline vs. online</big></bold></p></section>

 

<p>

    The standard IF library works slightly differently between online

    (over the Internet) and offline (on the local PC only) usage.

    Throughout the library it checks for ShardParamIsOffline(); you

    can search through the code for it. The offline parameter

    causes the IF to act like an adventure game, while the online

    is more like a MUD.

</p>

 

<p>

    Here are some differences:

</p>

 

<ol>

    <li>

           The offline game only allows one player at a time. Online

           supports a thousand or more players.

    </li>

    <li>

           When the player logs off, the offline version will automatically

           shutdown the server. If online version will keep the server

           running until another user logs on.

    </li>

    <li>

           The offline code saves all the objects into a saved game

           file using SavedGameXXX() function, and the master SaveGame()

           function. The online code only saves the player characters

           and the objects held by the player charaters; these are saved

           in the database.

    </li>

    <li>

           The offline code supports the "Save" command. Online does not.

    </li>

</ol>

 

<p>

    Tip: If you want to run your IF server offline (without requiring

    the Internet) but you wish it to act is though it were on

    the Internet, then create a shard that connects to IP address

    "127.0.0.1"... which is basically an IP address only available

    within your computer.

</p>

 

 

 

 

<br/><section><p><bold><big>Put your sever online</big></bold></p></section>

 

<p>

    If you wish your players to access your IF title over

    the Internet (enabling multiple players to play in one world,

    and providing extra protection against piracy), then

    distribute the <bold>.crk</bold> file to them. This

    is a small file (only a few KB). The only information it

    stores is what you entered into the TitleInfo resource.

</p>

 

<p>

    For your players to use it, they must download

    the <bold>.crk</bold> file, and download and install

    the Circumreality client. When they run the Circumreality client they need

    to select the .crk file you distributed. If you have more

    than one shard,they will be asked which shard to use,

    and then the Circumreality client will connect to your server over

    the Internet. It uses the domain name or IP address you

    entered into the TitleInfo resource.

</p>

 

<p>

    On your side, you must have a server (fancy term for a computer)

    running and connected to the Internet. The computer's Internet

    address must be the one provided in the TitleInfo resource,

    which was also stored in the <bold>.crk</bold> file.

</p>

 

<p>

    You then need to run the CircumrealityWorldSim.exe and have it run your

    IF title. The best way to do this is to provide a command

    in the "Run..." dialog (from the Windows Start menu) with

    the following <bold>"c:\program files\mXac\Circumreality\CircumrealityWorldSim.exe"

    -1 c:\MyIFFiles\MyIFTitle.CRF</bold>.

</p>

 

<p>

    You'll need to replace <bold>c:\program

    files\mXac\Circumreality\CircumrealityWorldSim.exe</bold> with whatever the full path

    for the CircumrealityWorldSim.exe file is. The <bold>quotes</bold> around the

    server's path are very important since they prevent the

    operating system being confused by the space in "program files".

</p>

 

<p>

    <bold>c:\MyIFFiles\MyIFTitle.CRF</bold> should be replaced

    with the location of the .CRF file for your IF title.

</p>

 

<p>

    The <bold>-1</bold> tells the server to run using the

    parameter for Shard #1. <bold>-2</bold> will use Shard #2's

    parameters, etc.

</p>

 

<p>

    You will probably want your server to run the command line

    as soon as it starts up, so that even if the server reboots

    your IF title will be running.

</p>

 

<p>

    That's it.

</p>

 

 

<br/><section><p><bold><big>Quick test with friends</big></bold></p></section>

 

<p>

    If you just want to do a quick test with some friends,

    and don't have a dedicated server for your IF title:

</p>

 

<ol>

    <li>

           In the <bold>TitleInfo</bold> resource, create a shard

           whose address is a bogus web site, such

           as <bold>http://www.NotARealWebSite.com</bold>.

    </li>

    <li>

           Go through all the compiling and distribute

           the <bold>.crk</bold> file to your friends.

    </li>

    <li>

           When you wish to test your IF, <bold>connect

           to the Internet</bold> and <bold>run CircumrealityWorldSim.exe</bold> from

           the command line, as from above. Make sure the shard number

           in the <bold>-1</bold> points to the shard with the

           bogus web site.

    </li>

    <li>

           When the Circumreality server window appears, it will display the

           server's IP address in the title. It will be a number

           separated by periods, like <bold>12.34.567.89</bold>.

    </li>

    <p>

           If the listed IP address is <bold>127.0.0.1</bold> or <bold>0.0.0.0</bold> then

           the server either couldn't connect to the Internet or (for whatever

           reason) wasn't able to get the computer's IP address.

           Retry and hope it works, or you're out of luck. (Although there

           are ways to get the computer's IP address from the network control

           panel and other applications. You can

           also try <a href="http://www.WhatIsMyIP.com">http://www.WhatIsMyIP.com</a>)

    </p>

    <li>

           Call your friends and tell them to run the client on their

           computer. When they select your special shard (with the

           bogus web site) they'll be asked for an IP address. Have

           them type in the IP address from the server's title.

    </li>

    <p>

           They should connect to your server.

    </p>

</ol>

 

<p>

    NOTE: If you turn off your computer (or log off the Internet),

    your IP address will probably be different the next time you

    log on.

</p>

 

 

 

 

 

 

31 Getting your world hosted by a third party

The effort needed to get your world hosted third party.

 

<section><p><bold><big>Getting your world hosted by a third party</big></bold></p></section>

 

<p>

    The chances are that you won't want to leave your computer up

    and running all the time, nor will your ISP like you using several

    hundred gigabytes of bandwidth a month.

</p>

 

 

 

<br/><section><p><bold><big>Finding a host</big></bold></p></section>

 

<p>

    To find a host, you should google for "virtual private server" (or "VPS"),

    "hosting", "private server", "dedicated hosting", etc.

</p>

 

<p>

    For my part, I chose www.Prohosters.com. As of writing this, I've only

    used them for a few days, so I can't really give an opinion other

    than their product support personel have been very helpful so far.

    I decided to use them because they were mid-priced; I'm always

    wary of the cheapest products.

</p>

 

<p>

    From Prohosters (and others), you have two choices:

</p>

 

<ul>

    <li>

           <bold>Dedicated server</bold> - A computer is set aside specially

           for you. Only your software is running on it. This is the ideal

           solution, but it's also expensive, $200+ per month (as of this

           writing). If you were expecting hundreds of simultanous players,

           this would be a requirement.

    </li>

    <li>

           <bold>Virtual private server (VPS)</bold> - In a VPS, you share one

           computer with many other users, sometimes as many as 60.

           The up-side side this that VPS's

           are affordable for amateurs, starting at around $50 per month.

           Unfortunately, you have less memory and CPU since the computer's

           resources are shared amongst all the users. This isn't an issue

           for amateur worlds since you'll be lucky to have 50 players online

           at once.

    </li>

    <p>

           However, one annoyance with VPS servers is that with 60 users,

           there's a <bold>much</bold> greater chance of a server crash.

           This means that your world won't have 99.9% up-time. Again,

           amateur worlds can get away with downtime, but professional

           ones cannot.

    </p>

</ul>

 

<p>

    I chose prohoster's $70/month package to start out with. It's

    a Windows 2003 server running Plesk. It provides me with 256 megabytes

    of RAM (up to 1 gigabyte burst RAM), 10 gigabytes of

    storage, 500 gigabytes of bandwidth. Under this plan, one server is

    divided amongst 30 users.

    My guestimate is that such a package would support 25-50 simultaneous

    players, which is plenty for an amateur world, or for my

    initial test world.

</p>

 

<p>

    Here's a general idea of what your hosting service needs to

    offer, for an amateur world:

</p>

 

<ul>

    <li>

           <bold>Windows</bold> 2003, XP, or Vista.

    </li>

    <li>

           <bold>256-512 megabytes RAM</bold> assigned to you. 5-10 gigabytes of hard drive.

           About <bold>one-tenth to one-fifth of a CPU-core</bold>. The Prohoster machine seems to have

           four processors, divided by 30 users, is about 13% of a CPU core

           per user.

    </li>

    <li>

           <bold>SMTP (and E-mail) server</bold> since CircumReality will send E-mail to

           players when the receive in-game mail. You don't need to have the same

           service providing the E-mail though. This seems to be a standard option.

    </li>

    <li>

           Access to the server via Windows' <bold>Remote desktop connection</bold>. This

           seems to be a standard option.

    </li>

    <li>

           The ability to have the CircumRealityWorldSim.exe program <bold>automatically

           restart if the server reboots</bold>; this is particularly important for VPS (cheaper)

           servers since they seem to reboot every few days. Windows comes with this

           functionality built in, and it should be standard for hosting services.

    </li>

    <li>

           <bold>A domain name</bold>, like www.MyWorld.com. You don't really need this

           since CircumReality will also work with just an IP address. Domain names

           are standard.

    </li>

</ul>

 

 

 

 

<br/><section><p><bold><big>E-mail</big></bold></p></section>

 

<p>

    CircumReality sends E-mail to players to notify them when they get in-game

    mail, when their user accounts are about to be automatically

    deleted, and when players forget their password.

</p>

 

<p>

    For this to work you must:

</p>

 

<ol>

    <li>

           <bold>Set up an E-mail server</bold>. Hosting services assume that people

           will automatically want their own E-mail servers when they subscribe,

           so E-mail is a given.

    </li>

    <p>

           Unfortunately, setting up an E-mail server isn't necessarily as easy

           as it should be. ProHosters uses Plesk. In Plesk, I can to create assing

           my IP address(es) to the IP pool, and then create a "domain" that was

           associated with one or more of the IP addresses in the pool. Then I had

           to create an E-mail account, which didn't work right away... but

           product support got it working eventually.

    </p>

    <p>

           Once you set up your E-mail server, you need to find out the address/name

           of the SMTP server, and your SMTP-authentication name and password.

           (SMTP is "Simple mail transfer protocol" for sending mail, not receiving it;

           POP is for receiving, but CircumReality doesn't recieve E-mail, only sends

           it.)

           This information is the same stuff you type into Outlook Express (or

           whatever E-mail program you're using) under the "Accounts" option.

    </p>

    <li>

           Once you get all the right addresses for your SMTP server, <bold>you

           need to set the following globals:</bold> gBugEmailAddress, gBugMailToCharacter,

           gEmailServerName, gEmailAccessPasswordEmail,

           gEmailMailSendEmail, gEmailSMTPServer,

           gEmailDomain, gEmailServerName, gEmailAuthUser, and gEmailAuthPassword.

    </li>

    <p>

           This might be a good point to set gEULACompanyName with your company's name,

    </p>

    <p>

           You also need to set the string, sMailSendGameMailName.

    </p>

</ol>

 

 

 

 

<br/><section><p><bold><big>TitleInfo resource</big></bold></p></section>

 

<p>

    You need to create a <bold>TitleInfo</bold> resource to override the default one,

    rTitleInfo. (It must use the same name too.)

    The resource contains a lot of information about your world, such as

    its name, a description of it, and a background picture.

</p>

 

<p>

    As far as this tutorial is concerned, what's really important is that you

    need to create a "shard" entry that lists the domain for your server.

</p>

 

<ol>

    <li>

           In the TitleInfo resource editor, <bold>click on the "Internet" tab.</bold>

    </li>

    <li>

           If a shard isn't already listed, then press <bold>Add a new shard</bold>.

    </li>

    <li>

           Type in a <bold>name</bold> for the shard. If your game only has one shard then

           this doesn't really matter because players won't see it.

    </li>

    <li>

           Type in a <bold>short description</bold> for your shared. Again, this is

           only important if you are going to run more than one shard.

    </li>

    <li>

           Type in the <bold>server</bold>. This <bold>is important</bold>, since without

           it CircumReality won't be able to locate your server. This the the domain

           name that's associated with your server, such as www.MyGame.com. Of course, you'll

           have to pay someone to reserver this name, probably the same

           company that provides your server.

    </li>

    <li>

           Type in the TCP/IP <bold>port</bold>. If you don't know what this is, or don't

           care,then leave it at its default, 4000.

    </li>

    <li>

           Type in a <bold>parameter</bold> that will be accessible through ShardParam(). This

           is only necessary if you are running more than one shard.

    </li>

</ol>

 

 

<br/><section><p><bold><big>Build your .crf (and .crk) files</big></bold></p></section>

 

<p>

    You must build a .crf and ,crk file:

</p>

 

<ol>

    <li>

           In the "Misc" menu, select <bold>"Compile"</bold>.

    </li>

    <li>

           In the "Misc" menu, select <bold>"Test compiled code"</bold>.

    </li>

    <li>

           A "CircumReality World Simulator" window will appear. It's

           not important now. <bold>Close it</bold>.

    </li>

</ol>

 

<p>

    Look in the directory where you store your project file, such as

    "w:\Scripts\MyProject.mfp". In that same directory, you'll find

    a newly-created "w:\Scripts\MyProject.crf", and

    "w:\Scripts\MyProject.crk". <bold>Copy these</bold> someplace safe.

</p>

 

<p>

    Then, <bold>upload</bold> both the .crf and .crk files to a FTP

    server that your hosted world-server can access. The easiest option

    is to upload them both to your world-server's FTP site. (How you

    get FTP working on your server is another issue.)

</p>

 

<p>

    The .crf file contains all the data and script necessary to

    run your world, as is probably 100 megabytes or more. The .crk

    file should be small, around 50 KBytes, and is the file you hand

    out to players who want to enter your world.

</p>

 

 

<br/><section><p><bold><big>Starting up your world</big></bold></p></section>

 

<p>

    To start your world (on your hosted server), you need to do the following:

</p>

 

<ol>

    <li>

           <bold>Run "Remote desktop connection"</bold>. It's hidden under

           your Start menu, in Programs, Accessories, Communication.

    </li>

    <li>

           You'll need to type in your server's <bold>domain</bold> as

           well as the <bold>account and password</bold> to long in is.

           Your domain will be www.MyGame.com, or whatever domain you've

           chosen. The account will probably be "Administrator", but not

           necessarily. The password will have been chosen by you

           at some point. ProHosters uses Virtuozzo, which lets you

           set the log-in password.

    </li>

    <p>

           When you do successfully log in, your desktop will be replaced

           by the desktop on your remote server. At the top of the screen

           is a small toolbar listing the server name and providing

           buttons for minimize and close.

    </p>

    <li>

           On your remote server, <bold>download and install CircumReality</bold> from

           www.CircumReality.com.

    </li>

    <li>

           On your remote server, either <bold>download MyProject.crf</bold> or

           find where FTP stored it on the sever by searching for MyProject.crf.

    </li>

    <li>

           <bold>Copy MyProject.crf</bold> to c:\, or some directory of your

           choosing.

    </li>

    <li>

           <bold>Open the "c:\program files\mXac\CircumReality" directory</bold>,

           or wherever CircumReality was installed.

    </li>

    <li>

           <bold>Run CircumRealityWorldSim.Exe</bold>.

    </li>

    <li>

           Press <bold>Dialog</bold>.

    </li>

    <li>

           Open up <bold>c:\MyProject.crf</bold> or whatever you used.

    </li>

    <li>

           Press the <bold>Open (and run)</bold> button.

    </li>

    <p>

           The "CircumReality World Simulator" window will appear. Your world is live!

    </p>

</ol>

 

 

 

 

<br/><section><p><bold><big>Some sanity checks</big></bold></p></section>

 

<p>

    To make sure your world is working:

</p>

 

<ol>

    <li>

           On your local computer (not your server), <bold>run CircumReality</bold>.

    </li>

    <li>

           If you have a <bold>password file</bold> then use that, otherwise create one.

    </li>

    <li>

           Press <bold>Try playing in a new world</bold>.

    </li>

    <li>

           In the world-selection page, find "w:\Scripts\MyProject.crk"

           (<bold>not</bold> the .crf file) and <bold>open it</bold>.

    </li>

    <p>

           Within a few seconds, <bold>you should be connected to your world</bold>.

    </p>

    <li>

           You can verify this by switching to the <bold>remote desktop</bold> and

           check that the <bold>"CircumReality World Simulator" shows a connection</bold>.

    </li>

</ol>

 

<p>

    If you can't connect, then try the following:

</p>

 

<ol>

    <li>

           Make sure you have <bold>the right domain name</bold> in rTitleInfo.

           It should be www.MyGame.com, or whatever you told the server company.

    </li>

    <li>

           From the command line, type <bold>"ping www.MyGame.com"</bold>. It should

           say that it worked; if it didn't then you need to talk to your server

           provider.

    </li>

    <li>

           The ping command will display an IP address for www.MyGame.com. This

           should <bold>be the same</bold> as the IP address shown by the

           "CircumReality World Simulator" running on the server (accessible through

           the remote desktop). If it isn't then talk to your server provider.

    </li>

</ol>

 

<p>

    Another thing to check is that the CircumReality World Simulator can send out E-mails:

</p>

 

<ol>

    <li>

           <bold>Run CircumReality</bold> from your local computer. You should already

           have an entry in your password file for "My project".

    </li>

    <li>

           Before clicking on the "Play My Project" button, <bold>hold down the control

           key</bold>. Then click on the button, then release the control key.

    </li>

    <p>

           This will log you on, but won't immediately go to play. Instead you'll be presented

           with a few options, one of which is "Change personal information".

    </p>

    <li>

           Press <bold>Change personal informatiion</bold>

    </li>

    <li>

           Type in your <bold>E-mail address</bold> if it isn't already typed in.

    </li>

    <li>

           Press <bold>E-mail password</bold>.

    </li>

    <p>

           Check your E-mail account a minute later. You should have received an E-mail with

           your password.

    </p>

</ol>

 

<p>

    <bold>If you didn't</bold>, then something isn't set up properly with your

    E-mail server. Check the following:

</p>

 

<ol>

    <li>

           Set up Outlook Express (or your E-mail program) to use the POP and SMTP settings

           for your E-mail server. Try <bold>sending an E-mail</bold> from your server's E-mail

           account to your main E-mail account. If this doesn't work then see

           your server company's support staff for help.

    </li>

    <li>

           <bold>Make sure</bold> that gEmailAuthUser, gEmailAuthPassword, etc. are

           entered properly.

    </li>

    <li>

           Assuming that you saved your .crf file as "c:\MyProject.crf" on the

           remote server, <bold>switch to</bold> "c:\MyProject" on the remove server.

    </li>

    <p>

           In the directory you'll find one or more files that look like,

           "log2007032621.txt". These are log text files. You'd normally access them

           from an administrator account in CircumReality, but since since you already

           have the remote desktop opened, this is just as easy.

    </p>

    <li>

           Find <bold>the most recent logXXX.txt file and double-click it</bold> to

           open it with notepad.

    </li>

    <p>

           Scroll down the the bottom of the text file, and look for text indicating that

           E-mail was set. For example: Search for "SMTP" or "Domain" in the text.

           Read then next 10-20 lines and look for some indication about what type of

           error occurred.

    </p>

</ol>

 

 

<br/><section><p><bold><big>Create the main administrator account</big></bold></p></section>

 

<p>

    By default, <bold>the first person</bold> to log into the world and create the user-name,

    "Administrator", as well as the character name, "Administrator", gets special priviledges.

    (You can change these defaults.)

</p>

 

<p>

    Therefore, you <bold>must</bold> create an "Administrator" user account, and an

    "Administrator" character within that account:

</p>

 

<ol>

    <li>

           <bold>Run CircumReality, and then select and type the password for your password file.</bold>

    </li>

    <li>

           Press <bold>I have an existing user acount in a world</bold>.

    </li>

    <li>

           In the world selection page, <bold>select w:\Scripts\MyProject.crk</bold> (not the .crf file).

    </li>

    <p>

           The "Account view/edit" page will appear.

    </p>

    <li>

           Under <bold>Name</bold>, type "Administrator account", or something that's easy to identify.

    </li>

    <li>

           Under <bold>User name</bold> type in "Administrator".

    </li>

    <li>

           Under <bold>Password</bold> type in a password. Make sure not to lose it.

    </li>

    <li>

           Press <bold>Back</bold>.

    </li>

    <li>

           <bold>Press "Play Administrator account"</bold>.

    </li>

    <p>

           You will log into your server.

    </p>

    <li>

           When asked for your <bold>character's name</bold>, type in "Administrator".

    </li>

    <p>

           That's it.

    </p>

</ol>

 

 

<br/><section><p><bold><big>Handling re-boots</big></bold></p></section>

 

<p>

    The server is guaranteed to reboot at some point. If you are using a virtual

    private server, it'll probably reboot every few days. To handle this circumstance:

</p>

 

<ol>

    <li>

           On the server (using the remote desktop), <bold>Find a select the

           "Schedule Tasks"</bold> option. In Windows, you'll normally find it

           under the Start menu, Programs, Accessories, System Tools. In the ProHosters

           server I was given, it was underneath "Control panel".

    </li>

    <p>

           When the "Schedule Tasks" list appear, it will show a list of

           scheduled tasks, along with an option for "Add scheduled task".

    </p>

    <li>

           Double-click <bold>Add scheduled task</bold>.

    </li>

    <li>

           The "Scheduled task" wizard will appear. Press <bold>next</bold> to

           get to the application-selection pane.

    </li>

    <li>

           You'll be shown a list of applications. Unfortunately, the right one

           isn't on them. Instead, click <bold>Browse</bold>.

    </li>

    <li>

           Find the "c:\program files\mXac\CircumReality" directory (or

           wherever you installed) and <bold>select CircumRealityWorldSim.exe</bold>

           by double-clicking it.

    </li>

    <li>

           The wizard will ask you when to perform the task. <bold>Clik on

           "When my computer starts" and press "Next"</bold>.

    </li>

    <li>

           You'll then be asked for your <bold>user name and password.</bold> This

           is the user name and password that you used for the "Remote desktop

           connection" program. Press <bold>Next</bold>.

    </li>

    <li>

           In the last pane, you'll be given an option for "Open advanced properties

           for this task when I click Finish". <bold>Check this</bold>.

    </li>

    <li>

           <bold>Press "Finish"</bold>.

    </li>

    <p>

           A new window wil appear with three tabs, "Task", "Schedule",

           and "Settings".

    </p>

    <li>

           In the "Task" tab, <bold>append " -1 c:\myProject.crf"</bold> to

           the "Run" line. After doing this, the line will probably

           be, "<italic>"C:\Program Files\mXac\Circumreality\CircumrealityWorldSim.exe"

           -1 c:\myProject.crf</italic>"

    </li>

    <li>

           In the "Settings" tab, make sure "Stop the task if it runs for:"

           is <bold>unchecked</bold>

    </li>

    <li>

           Press <bold>OK</bold>.

    </li>

</ol>

 

<p>

    You won't find out if this works until the server reboots and players start

    E-mailing you that the server is down. Some potential ways

    this can fail are:

</p>

 

<ul>

    <li>

           You <bold>mistyped something in the "Run" edit field</bold>. You can

           test this by copying the line to the clipboard, press the Start menu,

           then "Run...", pasting in the text, and pressing "OK". If

           the "CircumReality World Simualtor" doesn't appear a few seconds later

           then you probably have a typo. (Make sure that any running versions

           of the world simulator are shut down before trying this.)

    </li>

    <li>

           You <bold>may have typed in the wrong user and password</bold>.

    </li>

    <li>

           The scheduled task may not have gone off, perhaps because of <bold>permissions

           that the hosting company has set.</bold> Talk to their product support staff

           for help.

    </li>

</ul>

 

 

<br/><section><p><bold><big>Detecting failed reboots and crashes</big></bold></p></section>

 

<p>

    You can run a monitoring application to make sure that the CircumReality

    server is running properly. To do this:

</p>

 

<ol>

    <li>

           <bold>On the sever</bold>, using "Remote desktop connection, <bold>run CircumRealityWorldServer.exe</bold>.

    </li>

    <li>

           Press the <bold>Set up the monitoring mode parameters</bold> button at

           the bottom of the page.

    </li>

    <li>

           Press the <bold>Dialog</bold> for "World #1" and use it to find

           the .crf file, like "c:\myProject.crf".

    </li>

    <li>

           Type in the <bold>Shard number for world #1</bold>, which is probably "1".

    </li>

    <li>

           If you're running any other worlds on this server, repeat.

    </li>

    <li>

           <bold>Fill in all the Email info</bold> on the page. It's exactly the same information

           that you used to send notification E-mails from the server.

    </li>

    <li>

           You might want to <bold>uncheck "E-mail even if it's all running properly"</bold> after

           you have the system working. Leave it checked for now so that you know it's working.

    </li>

    <li>

           <bold>Close</bold> the window.

    </li>

    <li>

           Test that the program works by

           running <bold>"c:\program filex\mXac\CircumReality\CircumRealityWorldSim.exe

           -monitor"</bold>. It should run the monitoring application, potentially start up the

           server, potentially send E-mail, and then shut down.

    </li>

    <li>

           As in "Handling re-boots", <bold>create a timer</bold>. However, make this

           timer run <bold>once and hour</bold> (or so), and have it

           run <bold>"c:\program filex\mXac\CircumReality\CircumRealityWorldSim.exe

           -monitor"</bold>.

    </li>

</ol>

 

 

<br/><section><p><bold><big>Exiting the remote desktop by staying logged on</big></bold></p></section>

 

<p>

    That's it! Your world is running. One important note:

</p>

 

<ul>

    <li>

           When you close the "Remote desktop connection" window, <bold>you will stay logged

           onto Windows (on the remote computer)</bold>. This is good. If you log off,

           the CircumReality World Simulator will be closed, and the world will

           stop functioning... until you log back on and re-run it.

    </li>

</ul>

 

Documentation – Basic Administration Library

Code for basic administration commands.

 

This library handles many/most administrator commands and features that allow administrators to remotely monitor and control the IF title.

1 Administrator accounts

How to create administrator accounts.

 

<section><p><bold><big>Administrator accounts</big></bold></p></section>

 

<p>

    Online virtual worlds need administrators with special powers,

    such as the ability to kick off users.

</p>

 

<p>

    The <bold>user whose name is "Administrator"</bold> will automatically

    have a level 100 (maximum) for the pUserAdmin property.

    That means, <bold>you must log on and claim the user name

    "Administrator"</bold> before a player does.

</p>

 

<p>

    Once you log on as an administrator, any character used

    by the administrator will be able to use administration

    commands that allow you to create other administrator

    accounts. (See the online help for details.)

</p>

 

<p>

    If you want your master administrator account to have a

    different name then change gUserAdministrator.

</p>

 

<p>

    You should also <bold>create a character named "Administrator"</bold> that

    will receive game-mail send to "[Admins]" and complaints

    about griefers.

</p>

2 Safety measures

Some built-in crash protection.

 

<section><p><bold><big>Safety measures</big></bold></p></section>

 

<p>

    Circumreality contains some built-in crash protection code and settings.

    You might wish to adjust the following:

</p>

 

<ul>

    <li>

           <bold>gSafetyMaximumCPU</bold> - This is the maximum CPU that

           the server will (attempt) utilize. Once the CPU level reaches this

           amount then no more users will be able to log on. This defaults

           to 0.8 (out of 1.0) of the CPU.

    </li>

    <li>

           <bold>gSafetyMaximumMemory</bold> - If the server's memory footprint

           exceeds this amount then the server will automatically shut down

           and restart. This is protection against memory leaks of various

           sorts. This defaults to 1.5 GB, since Windows 32 runs out of

           memory at 2 GB.

    </li>

    <li>

           <bold>gSafetyMaximumNetwork</bold> - Use this to (approximately) limit the amount

           of bandwidth used. If the recent bandwidth usage is more than this

           value then users won't be able to log on. This is

           in megaBYTES per second. Thus, an 8 megabit line would be 1.0.

           A 56 kBaud modem is 0.007.

    </li>

    <li>

           <bold>gSafetyMaximumUserAccounts</bold> - If the number of user

           accounts is greater than this, then no more new-user accounts

           will be allowed. This defaults to 10,000.

    </li>

    <li>

           <bold>gSafetyMaximumUsersConnected</bold> - New users won't be able to

           log in if the number of connections is greater than

           gSafetyMaximumUsersConnected. This defaults to 500.

    </li>

    <li>

           <bold>gSafetyMaximumIP</bold> - How many users can be connected

           from the same IP address. This defaults to 4 to allow family members

           to play together.

    </li>

</ul>

3 Keeping performance statistics

How to keep performance statistics.

 

<section><p><bold><big>Keeping performance statistics</big></bold></p></section>

 

<p>

    If you want to keep statistics about your world's performance, such

    as the number of users logged on at any one time, the number of

    characters created, etc. then use

    the <bold>cStatistics</bold> class.

</p>

 

<ol>

    <li>

           <bold>Create a new object</bold> based on cStatistics.

    </li>

    <li>

           Set <bold>pNLPNounName</bold> and <bold>pNLPParseName</bold> to

           the name of your statistics, such as "Illegal password attempts".

    </li>

    <li>

           Set <bold>pExamineGeneral</bold> to a description of the

           statistics.

    </li>

    <li>

           <bold>pStatisticsDatasets</bold> and <bold>pStatisticsUnits</bold> should

           be filled with string labels so viewers of the data know what they're

           looking at.

    </li>

    <li>

           If you want players to be able to see the statistic then

           set <bold>pUserAdmin</bold> to 0. This defaults to 1, and is the

           minimum administration level necessary to see the statistic.

    </li>

    <li>

           Set <bold>pStatisticsSamplesPerDay</bold> to the number of numbers

           you wish to store in your statistics per day. In the case of

           illegal password attempts, you may only need one per hour, so

           a number like 24 would do.

    </li>

    <li>

           Normally, only the 1000 most recent entries will be kept. If

           you wish to keep more or fewer entries then

           change <bold>pStatisticsMaxEntries</bold>.

    </li>

    <li>

           Whenever an event occurs, such as a failed password attempt,

           then call <bold>pStatisticsIllegalPassword.StatisticsTick(1);</bold>.

           This will record the event.

    </li>

</ol>

 

<p>

    That's it. Administrators will now be able to see how many wrong

    passwords were entered, with an accuracy of once per hour.

</p>

 

 

<br/><section><p><bold><big>Statistics based on other statistics</big></bold></p></section>

 

<p>

    The illegal password statistics object will only keep 1000 entries, at

    once every 24 hours, is about 40 days worth. If you wished to keep

    several years worth, you could increase the maximum number of entries,

    but that would end up storing more data than you need.

</p>

 

<p>

    Alternatively, you can create a statisic, "slow" illegal password, that's

    based on the illegal password statistic and only takes a measurement once

    a day:

</p>

 

<ol>

    <li>

           <bold>Create a statistics object</bold> just like you created

           the one for illegal paswords, except...

    </li>

    <li>

           Set <bold>pStatisticsSamplesPerDay</bold> to 1, or 4 is you want to

           keep statistics ever 6 hours.

    </li>

    <li>

           <bold>Don't</bold> call StatisticsTick() for this object when

           a bad password is entered.

    </li>

    <li>

           Set <bold>pStatisticsBasedOn</bold> to oStatisticsIllegalPassword.

           This tells the new "slow" object to get all its information from

           the faster oStatisticsIllegalPassword, using StatisticsQuery().

    </li>

</ol>

 

<p>

    The result will be two statistics object, a "fast" one that measures

    data every hour, and a "slow" one that sums up the fast one's data

    once a day.

</p>

 

 

<br/><section><p><bold><big>Non-counter statistics</big></bold></p></section>

 

<p>

    To make a statistic that keeps track of values, such as the

    number of simultanous users logged on, as opposed to the number

    of users logging on in the last 10 minutes, you'll need to

    write some code:

</p>

 

<ol>

    <li>

           Write your own <bold>StatisticsSnapshot()</bold> method for the object.

           This will get any values, such as gConnectionList.ListNumber(), and

           return it. The returned value (or values) will be stored in the

           statistic object's database, along with a time stamp.

    </li>

    <li>

           You may need to write your own <bold>StatisticsStarted()</bold>. This

           method is called when the statistics object is first started, and can

           be used to initialize as settings.

    </li>

    <li>

           When a set of values are "combined", are the summed or averaged?

           In the case of the number of users logged on, you wish to average them,

           so set <bold>pStatisticsSumToPairDown</bold> to FALSE. If

           you were keeping track of the amount of data sent or received then

           you'd sum the values, so

           set <bold>pStatisticsSumToPairDown</bold> to TRUE.

    </li>

</ol>

 

<p>

    Again, easily done. And, just like with the counters, you can create

    a slower version of the number of users logged on.

</p>

 

 

 

<br/><section><p><bold><big>Other things</big></bold></p></section>

 

 

 

<ul>

    <li>

           <bold>StatisticsQuery()</bold> will get the statistical information

           from the statistics object.

    </li>

    <li>

           <bold>StatisticsStartStop()</bold> will start or stop keeping the

           statistics.

    </li>

    <li>

           If you want to keep track of how long a quest took, or when players

           started/stopped it, then create statistics objects based

           on <bold>cStatisticsQuestDuration and/or cStatisticsQuestStartEnd</bold> and

           reference the objects in the quest's <bold>pQuestStatistics</bold>.

    </li>

</ul>

4 Improving text-to-speech

Using the speech log to improve text-to-speech

 

<section><p><bold><big>Improving text-to-speech</big></bold></p></section>

 

<p>

    Circumreality relies heavily on text-to-speech. Unfortunately, text-to-speech

    is of questionable quality because the AI and simulation needed to

    produce quality text-to-speech is a long-long ways away.

</p>

 

<p>

    However, you can take some steps to improve text to speech:

</p>

 

<ol>

    <li>

           <bold>Use less text-to-speech</bold> - I know this may sound strange,

           but minimizing the amount of synthesized speech helps. One of the reasons

           I added voice-chat to Circumreality was to minimize the amount that text-to-speech

           was used.

    </li>

    <li>

           <bold>Don't mix text-to-speech with real speech</bold> - One sure

           way to make text-to-speech sound lousy is to put it side-by-side

           with real recorded speech. However, if you still wish to use

           recorded speech, MIFL will let you do it.

    </li>

    <li>

           <bold>Use transplanted prosody</bold> - This technology takes a recording

           of your voice, "copies" the prosody (pitch, timing, and volume) from the

           recording and "pastes" it onto the synthesized voice. It significantly

           improves the quality of text-to-speech, but it's a <bold>lot</bold> of work,

           and is only useful for pre-canned phrases. Automatically generated phrases

           can't be pre-recorded for text-to-speech.

           The <bold>speech log</bold> is useful for transplanted prosody. See below.

    </li>

    <li>

           <bold>Use automatic transplanted prosody</bold> - See below.

    </li>

    <li>

           <bold>Use quick transplanted prosody</bold> - See below.

    </li>

    <li>

           <bold>Create a prosody model</bold> - Part of the reason why text-to-speech

           sounds so bad is because its prosody (pitch, timing, and volume) are so

           wrong. Prosody is wrong because the text-to-speech system doesn't really

           understand what it's speaking, so it doesn't know what words to emphasize.

    </li>

    <p>

           You can "Create a prosody model" specific to the text your game world uses.

           It will require you to record 500-1000 phrases that are commonly spoken

           by the text-to-speech engine, and then learns the prosody for those

           phrases, or ones similar.

    </p>

    <p>

           The <bold>speech log</bold> is useful for transplanted prosody.

           Also, the <bold>Easy recording</bold> tool might come in handy. See below.

    </p>

    <li>

           <bold>Make your own TTS voice</bold> - Making your own text-to-speech voice

           is a <bold>lot</bold> of work (about 5000 recorded phrases), but will help for two reasons.

    </li>

    <ol>

           <li>

                  In the act of recording your own text-to-speech voice, you  end

                  up creating a <bold>prosody model</bold>, as above.

           </li>

           <li>

                  Text-to-speech synthesizes a word, like "lantern", <bold>by attaching

                  smaller recordings it copied from other words</bold>. For example: "lantern"

                  may be constructed from the "lan" part of "land" and the word "turn" that

                  were recorded in the training sentences. If a recording of the word

                  "lantern" exists in the training sentences, then that will be used

                  in preference to "lan(d)" + "turn". A recording of the original word

                  will sound better than combining two smalelr bits.

                  However, "lantern" may not actually have been in the text-to-speech

                  training set, or may not have occurred enough to be considered important

                  enough to keep. If you create your own voice using the 5000 most-common

                  phrases used in your game world, the words that you

                  most commonly use will sound better

                  as a result.

           </li>

    </ol>

    <p>

           The <bold>speech log</bold> is useful for transplanted prosody. See below.

    </p>

</ol>

 

 

<p>

    The <bold>speech log</bold> keeps track of the 5000 most-commonly used

    text-to-speech phrases. You can use this list of phrases as (a) a

    way to decide what sentences to record for transplanted prosody,

    (b) sentences to record for your prosody model, or (c) sentences to

    record for your own text-to-speech voice.

</p>

 

<p>

    To see the list of phrases, type <bold>"Show speech log"</bold> using

    your administrator account. This will display the most common 2500 phrases,

    sorted by the most common ones first. (This assumes that your world has

    been running long enough to actually speak 5000 different phrases.) It doesn't

    display the last 2500 since they have probably only occurred once or twice,

    and aren't statistically signifcant.

</p>

 

<p>

    As a programmer, you should be aware of the following:

</p>

 

<ul>

    <li>

           <bold>oSpeechLog</bold> is the object that stores the speech

           log. It is a sub-class of cSaveToDatabase, so it will be

           saved away and reloaded.

    </li>

    <li>

           <bold>oSpeechLog.SpeechLog()</bold> is called whenever SpeakNarrator()

           and SpeakObject() are called. If you bypass these two functions you

           may wish to append call oSpeechLog.SpeechLog() yourself.

    </li>

    <li>

           <bold>gSpeechLogMax</bold> controls how many spentences will be stored

           in the speech log. You can lower or raise this value. 5000 is a

           good value, even if you are creating your own text-to-speech voice, since

           many of the sentences you record for text-to-speech should be

           "phonetically balanced" (look it up on the internet) and not taking

           from your game's phrases.

    </li>

</ul>

 

 

<br/><section><p><bold><big>Easy recording</big></bold></p></section>

 

<p>

    The chances are, you'll be play-testing your IF title and hear text-to-speech

    speak a phrase that ends up sounding lousy because of the prosody.

    You would always run M3DWave.exe and record the wave then and there, but

    there's an easier solution:

</p>

 

<ol>

    <li>

           When you first run CircumrealityWorldSim.exe, check

           the <bold>Enable tools in the client</bold> checkbox at the bottom of the

           page. This will enable some author-specific tools in the client that

           are normally hidden from typical players.

    </li>

    <li>

           In the client, select the "Settings &amp; Options" menu item,

           following by the "Speech settings" dialog. In

           that dialog, type in a <bold>Record-wave-to directory</bold> where

           you want any recorded waves to be saved, such as "c:\\MyDirectory".

    </li>

    <p>

           If you're recording waves for your own text-to-speech voice then

           type in the directory where all the TTS recordings are saved. If

           you want to the use wave for a prosody model, type in that

           directory. Or, if you're using automatic transplanted prosody (see

           below), then type in the directory where all those files go.

    </p>

    <li>

           Whenever you hear a text-to-speech phrase that has lousy

           prosody, <bold>bring up the transcript window</bold>. Next to

           each sentence, you'll see a red link for "(Record)". To

           record a version of that sentence, press <bold>(Record)</bold>.

           The file will automatically be saved in the "Record-wave-to

           directory".

    </li>

    <p>

           If you are using the wave file for a text-to-speech voice or a

           prosody model, you'll still need to run MNLP.exe

           and <bold>add</bold> the recording to the list of wave files

           used for the voice or model. Assuming that the directory where

           you saved the file is only meant for the specific text-to-speech voice or

           prosody model, you can press the "Add all visible wave files" at the

           bottom of the wave-file open dialog box.

    </p>

    <p>

           Likewise, if you're using the wave file for transplanted prosody,

           you'll need to rebuild the automatic transplanted prosody resources.

    </p>

</ol>

 

 

 

<br/><section><p><bold><big>Quick transplanted prosody</big></bold></p></section>

 

<p>

    The MIFL editor makes it easy to record transplanted prosody for strings and

    spoken strings in some resources (such as conversation scripts, cut scenes, and

    speak-scripts.)

</p>

 

<p>

    Whenever you modify a string, or other appropriate resource, you'll see

    options for "Spell check", "Speak", "Record transplanted prosody", and

    "Delete trans. pros.".

</p>

 

<ul>

    <li>

           <bold>"Spell check"</bold> makes sure all of the words are in the

           text-to-speech voice's vocabulary. If they aren't, they'll probably be

           mispronounced, and you'll need to add them using 3D Outside the Box's

           "Natural Language Processing" application.

    </li>

    <li>

           <bold>"Speak"</bold> has the text-to-speech engine speak the

           phrase <bold>without</bold> using transplanted prosody. This will help

           identify words not in the voice's vocabulary, as well as detect

           gramatical mistakes.

    </li>

    <li>

           <bold>"Record transplanted prosody"</bold> lets you easily record a

           transplanted prosody resource for the string. The resource is named,

           "rTransProsQuick_XXX", where XXX is a randomly generated number.

           Whenever you use Speak(), SpeakScript(), SpeakNarrator(), etc., the

           transplanted prosody that you just recorded will be used.

    </li>

    <p>

           This is the <bold>easiest way to use transplanted prosody</bold>.

    </p>

    <li>

           <bold>"Delete trans. pros."</bold> deletes a rTransProsQuick_XXX resource

           for the given text.

    </li>

</ul>

 

 

<br/><section><p><bold><big>Automatic transplanted prosody</big></bold></p></section>

 

<p>

    If you're trying desperately to improve the quality of a text-to-speech

    voice, you should first try to use "Easy recording" (above) to record

    wave files for a prosody model.

</p>

 

<p>

    However, a prosody model may not be able to speak a specific sentence with

    the correct and subtle prosody. If that's the case then the next step

    is to use "Automatic transplanted prosody".

</p>

 

<p>

    To use "Automatic transplanted prosody", do the following:

</p>

 

<ol>

    <li>

           Use "<bold>Easy recording</bold>" (above) to record problem sentences as .wav files.

           Store them all in the same directory.

    </li>

    <li>

           <bold>Create a new library</bold> in your project where you will store

           the automatic transplanted prosody resources.

    </li>

    <li>

           In the <bold>Misc menu</bold>, select the <bold>Automatic

           transplanted prosdy</bold> tool.

    </li>

    <li>

           <bold>Fill in</bold> all the fields, especially the directory where

           you have saved all the troublesome .wav files.

    </li>

    <li>

           Press <bold>Create transplanted prosody resources from .wav files</bold> and

           the computer will process for awhile. (If you have just recorded some

           new .wav files, this may take a long time.)

    </li>

    <li>

           Switch to your <bold>List of resource</bold>, also in the "Misc" menu.

    </li>

    <p>

           You will see a number of new resources labelled "rTransProsAutoXXXXX", where

           XXXXX is replaced by a number. These are the transplanted prosody resources

           automatically generated by the .wav files you stored in the directory.

    </p>

    <li>

           When you have a NPC <bold>SpeakScript()</bold>,

           or the narrator <bold>SpeakNarrator()</bold>, if

           the text <bold>exactly</bold> matches the transplanted prosody text, then

           the transplanted prosody resource will be spoken instead of the text.

    </li>

    <p>

           You should be able to hear the difference in quality. If you're not 100%

           convinced, then have the NPC speak the text string, but with an added space,

           so the automatic transplanted prosody resource won't be used.

    </p>

</ol>

 

 

5 Log-on notices

Showing a notice when players log on.

 

<section><p><bold><big>Log-on notices</big></bold></p></section>

 

<p>

    If you want to show a notice when players log on, such as, "The mountains

    are under rennovation. Please E-mail me if you find any bugs," then

    do the following:

</p>

 

 

<ol>

    <li>

           Override <bold>rLogonMainNotice</bold> with your own notice.

    </li>

    <li>

           Change <bold>gNoticeVersion</bold> to a new number so that players

           will be shown the notice. (A notice will only be shown once, and the

           unique ID in gNoticeVersion is used to determine if the notice has

           already been seen.)

    </li>

</ol>

 

 

Documentation – Basic communications Library

The basic communications library provides internal E-mail (sometimes called mud-mail) and bulletin board facilities for users.

 

01 Message boards

Tells you how to add one or message boards to your online world.

 

<section><p><bold><big>Message boards</big></bold></p></section>

 

<p>

    Adding message boards to your online world is very easy:

</p>

 

 

<ol>

    <li>

           <bold>Create</bold> a new object and base it

           off of <bold>cMessageBoard</bold>.

    </li>

    <li>

           Make sure the <bold>Automatically create as an object</bold> button

           is checked. The message board <bold>should not</bold> be located in

           a room though.

    </li>

    <li>

           Set the <bold>pNLPNounName</bold> and <bold>pNLPParseName</bold> with

           the message board's name, like "Suggestions".

    </li>

    <li>

           Set <bold>pExamineGeneral</bold> to a string describing the message board.

    </li>

    <li>

           <bold>pMessageBoardDatabase</bold> should be filled with a unique database

           name for the message board where the message thread objects will

           be stored. This string will be passed into the DatabaseXXX() functions.

           You might wish to prefix the string with "MessageBoard" so the database is

           obviously a message board.

    </li>

</ol>

 

<p>

    That's it. You're done!

</p>

 

 

<p>

    If you wish to change the priviledges for the message board then

    write a <bold>MessageBoardCanAcces()</bold> method for the object.

</p>

 

Documentation – Basic RPG Library

Provides functionality to handle standard CRPG features.

 

The Basic RPG Library provides functionality to handle standard CRPG features, such as attributes, skills, combat, combat with NPCs, talking to NPCs, etc. It also handles a lot of MUD-style functionality.

 

This library is based on the Basic Interactive Fiction Library, which must also be included in the project. It must have a higher priority than the Basic Interactive Fiction Library.

 

01 Skills, attributes, and disadvantages

Describes how to define new skills, attributes, and disadvantages, and how to use them.

 

<section><p><bold><big>Skills, attributes, and disadvantages</big></bold></p></section>

 

<p>

    The RPG library does <bold>not</bold> support class levels,

    as is typical in Dungeons &amp; Dragons. Instead, it supports

    skills, allowing players to advance their characters in any

    direction they like.

</p>

 

<p>

    There are three types of skills:

</p>

 

<ul>

    <li>

           <bold>Skills</bold> - These are learned abilities, such as

           swordfighting, archery, and basket weaving. Skills can be

           learned by the characters and trained over time to improve

           them; training is done using experience points.

    </li>

    <li>

           <bold>Attribute</bold> - Attributes are characteristics that

           are fundamental to a character and cannot be changed (under

           normal circumstances.)

    </li>

    <li>

           <bold>Disadvantages</bold> - Disadvantages are, in many

           ways, negative skills. They are something the character begins

           with, but can pay off over time and get rid of. Some sample

           disadvantages are: fear of spiders, studdering, and alcoholism.

           Disadvantages are useful role-playing devices.

    </li>

</ul>

 

<p>

    Role playing games often have "special abilities" and "feats".

    These could be categorized under skills too.

</p>

 

 

 

<br/><section><p><bold><big>Create your own attributes, skills, and disadvantages</big></bold></p></section>

 

<p>

    The "Basic RPG library" and "Basic Fantasy library" include some

    standar attributes, skills, and disadvantages. You can always

    create your own. Here's how:

</p>

 

<ol>

    <li>

           <bold>Create</bold> a new object, called "oSkillXXX", where XXX

           is replaced with your skill's name.

    </li>

    <li>

           Base the skill off the super-class, <bold>cSkill</bold>. The

           cSkill class is a sub-class of cObject.

    </li>

    <li>

           Check the <bold>Automatically create as an

           object</bold> button. All skills are instantiated objects.

    </li>

    <li>

           Fill in the <bold>pNLPNounName</bold> and <bold>pNLPParseName</bold> for

           the skill with an appropriate name,

    </li>

    <li>

           Provide a description for the skill in <bold>pExamineGeneral</bold>.

           This description is displayed when the user types the "skills" command.

    </li>

    <li>

           Fill in the skill's <bold>pSkillCategory</bold> so that the

           skill will be placed in the appropriate category in the skills

           list, such as "Combat skills", or "Spells".

    </li>

    <li>

           If this is an attribute, set <bold>pSkillType</bold> to <bold>'a'</bold> (case

           is important). Attributes cannot be trained, and are randomly

           generated for a character when it is first created. You

           should also modify <bold>gAttributes</bold> to include the skill

           object.

    </li>

    <p>

           If the skill is really a disadvantage,

           set <bold>pSkillType</bold> to <bold>'d'</bold> (case

           is important). Users cannot train their characters with a

           disadvantage, so you will need to modify the character creation

           code so that disadvantages will be randomly applied or the

           player will be able to select the character's disadvantage.

    </p>

    <p>

           If the skill is a normal skill, don't set <bold>pSkillType</bold> since

           the default value, set in cSkill, is <bold>'s'</bold>.

    </p>

    <li>

           The <bold>pSkillLevelCap</bold> is normally set to a reasonably

           high value, around 30. However, you can make this lower or higher.

           If the value is low, the skill acts more like a "feat", where the

           skill is either not known, or known completely. (For example: Riding

           a bicycle.)

    </li>

    <li>

           You may wish to set <bold>pSkillTrainTeacherLevel</bold>. A player

           with a high enough level in a skill can teach the skill to

           other players. This is a number indicating the required skill

           level.

    </li>

    <p>

           If <bold>pSkillTrainTeacherLevel</bold> is set to 0, any

           characters can learn the skill without a teacher. You might

           wish to use a 0-value for skills like climbing or jumping, where

           the skills are just a matter of practice.

    </p>

    <p>

           To prevent players from training the skill to

           thousands of other players, the teacher <bold>loses</bold> one

           experience point every time they train another character.

    </p>

    <li>

           To make the skill require another skill, or an attribute,

           set <bold>pSkillTrainPrerequisites</bold>. This can contain

           zero or more skills with required levels.

    </li>

    <li>

           If the skill is associated with a language,

           set <bold>pSkillLanguage</bold> to the language-independent

           (and case sensative) language string, such as "common" or "elvish".

           If it's not a language, leave pSkillLanguage blank or set to NULL.

    </li>

    <li>

           If the language is an animal or monster language, you might

           wish to write a <bold>LanguageSoundEffect()</bold> for the skill

           so that when the animal/monster speaks to players that don't

           understand the language, a wave file will be played (like a growl sound)

           instead text-to-speech.

    </li>

</ol>

 

<p>

    Those are the basics of creating a new skill. There is more,

    but I'll get to the advanced topics after discussing experience points.

</p>

 

 

 

<br/><section><p><bold><big>Experience points</big></bold></p></section>

 

<p>

    Every skill has a <bold>skill level</bold>. Skills begin at level 0,

    and are increased as the user trains the skill using up

    earned experience points.

</p>

 

<p>

    The amount of experience points to raise a skill to the next

    level is (roughly) the level of the skill. The actual equation

    is that the <bold>level of the skill =

    sqrt(experience points invested)/2</bold>.

    This makes is more and more difficult for skill levels to improve.

</p>

 

<p>

    As a general guideline, 1 XP is handed out for every hour

    that the player plays. <bold>1 XP = 1 hour of play</bold>.

    Therefore, if a player only raises one skill, they can go

    from level 0 to level 10 in about 50 hours of play.

    You are, of course, welcome to hand out experience points

    any way you wish.

</p>

 

<p>

    In online (multiplayer) mode, the default behavior is to

    automatically hand out 1 XP for every hour the player is logged

    on (even if they're chatting). This is limited to no more than 1XP

    per day, and 3 XP per week. That means, that barring additional

    XP from quests, a player can get a maximum of 156 XP per year.

</p>

 

<p>

    In offline (single player) mode, experience points are handed out

    based on what quests/tasks the player completes. To

    provide XP in either offline or online mode,

    call <bold>XPAward()</bold>.

</p>

 

<p>

    The RPG library also includes an <bold>XPPenalize()</bold> call

    that will penalize the player if they act out of character. When

    the player has been penalized, his character can receive no

    experience for the next 24 hours. An example of penalization would

    be if the player's character has a "fear of heights" disadvantage

    but persisted in having his character walk across a tall bridge

    despite warnings.

</p>

 

<p>

    A player's attributes (or even other skills) affects how much

    1 XP helps a skill. Each skill has a <bold>pSkillAttribiteBase</bold> property

    that can list zero or more attributes. You can use this so that

    a higher attribute makes the experience go further towards advancing

    the skill. For example: A high intelligence would make learning

    a mathematics skill easier, thus making XP count for more. If

    the same character had a low strength attribute, strength-based

    skills would not advance as quickly.

</p>

 

 

 

<br/><section><p><bold><big>Attributes and standard deviations</big></bold></p></section>

 

<p>

    In Dungeons &amp; Dragons, attributes are rolled on 3 6-sided dice,

    resulting in a value between 3 and 18, with the average value

    being 10.5. The distribution of the numbers forms a <bold>bell curve</bold>,

    with attribute values in the 8-12 range being much more common than

    those at the extremes.

</p>

 

<p>

    A bell curve is a non-technical term for a mathematical

    entity known as a <bold>gaussian</bold>. The neat thing about

    bell curves, as mentioned above, is that most people's strength (or

    whatever) ends up being near the norm, but a very are very strong

    or very weak.

</p>

 

<p>

    The topic of bell curves brings up <bold>standard deviations</bold>.

    A standard deviation says how close an attribute is close to the

    norm. 68% of all characters will have a strength attribute

    within <bold>1 standard deviation</bold> of the norm. 95%

    will be within 2 standard deviations, 99% for 3, and 99.99% within

    4 standard deviations.

</p>

 

<p>

    In English terms, a world champion body builder is about 5 standard

    deviations above normal (stronger than 99.9999% of the population).

    Likewise, a exceptionally weak person is -5 standard deviations,

    or weaker than 99.9999% of the population.

</p>

 

 

<p>

    The RPG library <bold>stores attributes in standard deviations</bold>,

    not values between 3 and 18. Therefore, most characters' attributes

    will be close to 0 (meaning average), with ranges between

    -1 and 1. Some will be higher 1 to 2 or lower (-1 to -2) meaning

    above/below normal strength (or, dexterity, etc.) but nothing

    too unusually. A rare character will have values higher than 2, or

    lower than -2.

</p>

 

<p>

    Storing attributes as standard deviations has many handy ramifications

    that will become apparent as you read through the tutorials.

</p>

 

<p>

    When a new character (based in cCharacter) is created, the constructor

    automatically "rolls" random values for the attributes

    in <bold>gAttributes</bold>. It uses the <bold>RandomStdDev()</bold> function

    to generate the values. The attributes are also "normalized" so

    that the sum of all the character's attributes are 0... so as

    not to create any hopeless characters.

</p>

 

 

 

<br/><section><p><bold><big>Races and skills</big></bold></p></section>

 

<p>

    While races won't be discusses completely until a later topic,

    I will touch on some issues here where races affect what skills

    a character has.

</p>

 

<p>

    First of all, any race class (such as cRaceElf) is ultimately

    sub-classed from cCharacter. cCharacter supports several

    properties related to attributes, a few of which are relevant

    to races.

</p>

 

<ul>

    <li>

           <bold>pSkillsRacialInnate</bold> are a list of skills that

           are innate to a race. These include attribute adjustments

           (like all Elves having a +1 dexterity), or abilities

           that the race is born with (such as all Elves having a +5 magic skill).

    </li>

    <li>

           <bold>pSkillRacialLearned</bold> are skills that are automatically

           learned by the race. For example: All Elves being knowing cSkillElvish

           at level 10, and cSkillArchery at level 5, or whatever.

           When a character is first created, the character's constructor will

           move all the skills from pSkillRacialLearned into the character's

           skill list.

    </li>

    <li>

           <bold>pSkillsMaleInnate</bold> and <bold>pSkillsFemaleInnate</bold> work

           similarly to pSkillsRacialInnate except that they affect skills

           based on gender. There is no equivalent male/female test

           for pSkillRacialLearned though.

    </li>

</ul>

 

 

 

<br/><section><p><bold><big>Characters and skills</big></bold></p></section>

 

<p>

    Each character, based on cCharacter, has a number of properties

    and methods pertaining to skills:

</p>

 

<ul>

    <li>

           <bold>pSkills</bold> is a list of all the skills the character

           knows along with their skill level. As stated above, the

           character's constructor will add any skills

           from <bold>pSkillsRacialInnate</bold> that do not already

           appear in pSkills.

    </li>

    <p>

           If you write code to add or remove a skill from <bold>pSkills</bold>,

           don't forget to call <bold>SkillAdded()</bold> or <bold>SkillRemoved()</bold>.

           See the advanced skill topic below.

    </p>

    <li>

           As a general rule, you should <bold>not</bold> access <bold>pSkills</bold> directly,

           but instead use <bold>SkillGet()</bold> from the character object.

           SkillGet() is a useful method that gets the skill level

           from pSkills, and then <bold>adds</bold> the skill levels from

           <bold>pSkillsRacialInnate</bold> and <bold>pSkillsTemporary</bold> if

           there are any. This basically returns the skill level

           that includes character-specific skills/attributes,

           racial skills/attributes, and magic effect enhancements

           from pSkillsTemporary.

    </li>

    <li>

           <bold>pSkillsTemporary</bold> stores temporary modifications

           to skills, usually resulting from the possession or use

           of magic items. For example: A "Ring of Elvish Speech" would

           add the oSkillElvish skill to pSkillsTemporary while it

           was worn, allowing the user to speak elvish.

    </li>

    <li>

           Rather than modifying pSkillsTemporary directly, you

           should call the <bold>SkillTemporaryAdd()</bold> method,

           as well as <bold>SkillTemporaryQuery()</bold> and <bold>SkillTemporaryRemove()</bold>.

    </li>

</ul>

 

 

 

 

<br/><section><p><bold><big>Advanced skill topics</big></bold></p></section>

 

<p>

    Now that I have spend time discussing how skills fit in with

    characters, I'll delve into some advanced skill topics:

</p>

 

<ul>

    <li>

           Whenever a <bold>skill is added or removed</bold> from

           a character's <bold>pSkills</bold> or <bold>pSkillsTemporary</bold> properties,

           you should

           call <bold>cSkill.SkillAdded()</bold> or <bold>cSkill.SkillRemoved()</bold>.

           (You don't need to call these if you

           use <bold>SkillTemporaryXXX()</bold> since the functions are

           called automatically.)

    </li>

    <p>

           These calls allow the skills to set or remove timers and other

           hooks into the character object. The most common use for such

           hooks is disadvantage, where, for example, a cSkillAlcoholic

           disadvantage might require the character to drink some

           alchohol every 30 minutes of play or suffer an XPPenalize() call.

    </p>

    <p>

           Thus, if you wrote a cSkillAchoholic disadvantage, you'd

           provide a <bold>SkillAdded()</bold> method that would set

           a timer to go off every 30 mintes. <bold>SkillRemoved()</bold> would

           destroy the timer.

    </p>

   

    <li>

           You can write a <bold>SkillCanLearn()</bold> method for your

           skill that only allows it to be learned by certain races.

    </li>

   

    <li>

           If a skill allows a player to issue new command then

           you should provide a <bold>NLPCommandParseQuery()</bold>,

           <bold>NLPCommandParse()</bold>, and <bold>NLPRuleSetTempVerb()</bold> message

           to parse th new command. For example: If the skill were

           gymnastics, you might provide a "backflip" or "do a handstand"

           command for the user. Players who did not know the skill wouldn't

           be able to use the commands.

    </li>

</ul>

02 Body parts and wearing/wielding objects

Tells you how to make new body parts for characters, and allow objects to be worn or wielded by characters.

 

<section><p><bold><big>Body parts and wearing/wielding objects</big></bold></p></section>

 

<p>

    All RPGs allow characters to wield swords, don shields, and wear

    armor of various sorts. This requires the RPG library to know

    about body parts (such as legs, arms, torso, and head), and

    where items are worn on the body.

</p>

 

<p>

    In the RPG library, all characters have a list of <bold>body parts</bold>; the

    default human body parts are oBodyPartHead, oBodyPartTorso,

    oBodyPartArmLeft, oBodyPartArmRight, oBodyPartLegLeft,

    and oBodyPartLegRitht. Monsters or other races might have

    different body parts, including tails.

</p>

 

<p>

    Each body part has one or more <bold>body part locations</bold>.

    For example: The head has a location for a hat (or helmet),

    glasses, earrings, and necklace. Objects that are worn attach

    themselves to these body parts.

</p>

 

<p>

    Objects that can be worn maintain a list of body part locations

    that they can attach to. Usually this is only one location (such

    as the necklace), but some objects will connect to several

    locations (such as a jumpsuit, which covers the character's torso,

    arms, and legs).

</p>

 

 

 

<br/><section><p><bold><big>Creating body part locations</big></bold></p></section>

 

<p>

    The list of body part locations that are building into the RPG

    library are quite extensive. However, you may need to add your

    own body part locations. Here's how:

</p>

 

 

<ol>

    <li>

           <bold>Create</bold> a new object, called "oBodyPartLocXXX", where XXX

           is replaced with your location's name, such as "horn".

    </li>

    <li>

           Base the body part location off the super-class, <bold>cBodyPartLoc</bold>. The

           cBodyPartLoc class is a sub-class of cObject.

    </li>

    <li>

           Check the <bold>Automatically create as an

           object</bold> button. All body part locations are instantiated objects.

    </li>

    <li>

           Fill in the <bold>pNLPNounName</bold> and <bold>pNLPParseName</bold> for

           the skill with an appropriate name.

    </li>

    <li>

           Fill in <bold>pBodyPartLocDisplay</bold> with a string displayed

           when the object is equipped. This will normally be "Worn" or "Held",

           although you could always be more creative.

    </li>

    <li>

           Fill in <bold>pBodyPartLocEquipName</bold> with a list of command

           strings that can be used to equip the an item to the body part.

           Normally this is ["`wear"], allowing a user to type in "wear OBJECT", or

           "put on OBJECT". If you use ["`hold"] the user can type in

           "wield OBJECT" or "hold OBJECT". Right now, `wear and `hold are

           the only two strings accepted, but you can add more.

    </li>

    <li>

           Add the body part location object, oBodyPartLocXXX, to

           the appropriate body parts. See below.

    </li>

</ol>

 

 

 

<br/><section><p><bold><big>Creating body parts</big></bold></p></section>

 

<p>

    The chances are that you'll have to add new body parts, especially

    for monsters. The standard RPG library comes with humanoid body

    parts (heads, arms, legs, torsos), but doesn't include tails, wings,

    etc.

</p>

 

<p>

    To create a new body part:

</p>

 

<ol>

    <li>

           <bold>Create</bold> a new object, called "oBodyPartXXX", where XXX

           is replaced with your body part's name, such as "WingLeft".

    </li>

    <li>

           Base the body part off the super-class, <bold>cBodyPart</bold>. The

           cBodyPart class is a sub-class of cObject.

    </li>

    <li>

           Check the <bold>Automatically create as an

           object</bold> button. All body parts are instantiated objects.

    </li>

    <li>

           Fill in the <bold>pNLPNounName</bold> and <bold>pNLPParseName</bold> for

           the skill with an appropriate name.

    </li>

    <li>

           If the body part is on the left side of the creature,

           set <bold>pBodyPartRCL</bold> to -1. Use 0 if it's centered,

           or 1 if it's on the right side.

    </li>

    <li>

           Likewise, set <bold>pBodyPartTCB</bold> to 1 if the body

           part is on the top of the create, 0 for mid-level, and

           -1 for bottom.

    </li>

    <li>

           Set <bold>pBodyPartFCB</bold> to 1 if the body part is on

           the front of the create, 0 for mid, and -1 for back. Humanoids

           will use 0, but a centaur might have its human torso marked

           as being on the front, while the horse's  torso mid, and the

           rear legs and tail as back.

    </li>

    <li>

           <bold>pBodyPartLocations</bold> is a list containing the

           locations in the body part. Each location is a sub-list

           beginning with the body-part location object, followed by

           the shape string, and the weight scaling.

    </li>

    <p>

           The "body-part location object" is the oBodyPartLocXXX object.

           The shape string is a string that identifies the overall

           shape of the body part, such as "human". If you wanted an orc's

           head to be a different shape than a human's, so that orcs could

           not wear human helmets, then the shape would be "orc". Set

           the scale to 1.0, unless you have a combination creature like

           a centaur.

    </p>

    <p>

           For example: The pBodyPartLocations for the head might

           be: <bold>[[oBodyPartLocHat, "human", 1],

           [oBodyPartLocGlasses, "human", 1],

           [oBodyPartNecklace, "human", 1]]</bold>.

    </p>

    <li>

           You will need to write a <bold>BodyPartStatus</bold> function if

           the body part affects any the ability for the character to

           perform an action, such as legs affecting walking ability.

           For more information, see the next tutorial about damage.

    </li>

    <li>

           There are still more properties for body parts, but they

           deal with the damage model (which is the next tutorial).

    </li>

</ol>

 

<p>

    You will need to modify the class that defines the race (or

    creature) to include the body parts:

</p>

 

<ol>

    <li>

           In the race's class definition,

           set <bold>pBodyParts</bold> to a list of the body parts

           associated with the race. Each body part is a oBodyPartXXX

           object.

    </li>

    <p>

           Example: pBodyParts could be set to <bold>[oBodyPartHead,

           oBodyPartTorso, oBodyPartArmLeft,

           oBodyPartArmRight, oBodyPartLegLeft,

           oBodyPartLegRight]</bold>.

    </p>

    <p>

           Adding an extra oBodyPartArmLeft will make a 3-armed character,

           etc.

    </p>

</ol>

 

 

 

<br/><section><p><bold><big>Objects that can be equipped</big></bold></p></section>

 

<p>

    Making an object than can be equipped (worn, held, wielded, etc.)

    is easy:

</p>

 

<ol>

    <li>

           Set <bold>pEquipLoc</bold> to an oBodyPartLocXXX object if

           the object is equipped in only one body part (such as a helmet

           using oBodyPartLocHat), or provide a list of oBodyPartLocXXX

           for objects using several body parts. For example: A two

           handed sword would have [oBodyPartLocHeld, oBodyPartLocHeld],

           ensuring that it's held in two hands.

    </li>

    <li>

           If your object is normally equipped in the character's off hand,

           such as a shield, then set <bold>pEquipOffhand</bold> to FALSE.

           Otherwise, leave it blank.

    </li>

    <li>

           If you want your object to be equipped in body part locations

           of a specific shape, such as a helment that only fits orcs,

           then set <bold>pEquipShape</bold> to the shape string,

           such as "orc".

    </li>

    <li>

           To make the object only fit certain sized characters,

           set <bold>pEquipWeightMin</bold> and <bold>pEquipWeightMax</bold> to

           the character's minimum and maximum weights. This way

           you can make clothes that only fit small characters, etc.

    </li>

    <li>

           You can add futher restrictions to what characters can equip

           your item by providing your

           own <bold>EquipCanBeEquipped()</bold> method. For example: You

           might have a sword that can only be carried by character's

           named "Arthur".

    </li>

    <li>

           Whenever the object is equipped or unequipped, a

           call to <bold>MoveNotify()</bold> will be made. You can

           add a MoveNotify() method to your object so that its magic

           abilities will only activate when it's equipped.

    </li>

</ol>

 

<p>

    That's it, your object can be held or worn.

</p>

 

 

 

<br/><section><p><bold><big>Some useful methods</big></bold></p></section>

 

<ul>

    <li>

           <bold>Equip()</bold>, supported by cCharacter objects, will cause

           a character to equip an item.

    </li>

    <li>

           <bold>EquipCanBeEquipped()</bold>, supported by cObject objects, will

           indicate whether or not a character can equip the item.

    </li>

    <li>

           <bold>EquipUnEquip()</bold>, supported by cCharacter objects, will cause

           a character to unequip an item.

    </li>

</ul>

03 Damage model

Goes over what kinds of damage (wounds, etc.) characters can take.

 

<section><p><bold><big>Damage model</big></bold></p></section>

 

<p>

    Most role-playing games use the concept of "hit points" to

    indicate how much damage a character can take, either in combat

    or from other mishaps (such as falling). Circumreality does NOT use

    hit points, but instead uses a more realistic damage model

    with bleeding, flesh wounds, and broken bones. As a result,

    combat is more dangerous, and not something a player will

    enter into lightly.

</p>

 

<p>

    There are two basic types of damage: Damage which is systemic,

    like fatigue or bleeding, and damage which is localized to

    a body part (such as a flesh wound or broken bone). I'll

    being by discussing systemic damage since localized damage

    can cause systemic damage.

</p>

 

 

<br/><section><p><bold><big>Systemic damage</big></bold></p></section>

 

<p>

    A character's body can take several different forms of systemic

    damage:

</p>

 

<ul>

    <li>

           <bold>Bloodloss</bold> - Weapons create wounds in body parts,

           and wounds cause the character's blood to leak away slowly

           (or quickly). If enough blood is lost the character will fall

           uncsoncsious, and then die.

    </li>

    <li>

           <bold>Dazed</bold> - The character still standing, but not

           thinking clearly enough to do anything.

    </li>

    <li>

           <bold>Death</bold> - Of course. Player characters will be

           resurrected after a few minutes of death (during which time

           their bodies may be looted). NPCs will be deleted.

    </li>

    <li>

           <bold>Disease</bold> - Disease in Circumreality is treated more realistically

           than standard RPGs. Diseases take time to appear, and then

           bedrest (or magic) before they disappear.

    </li>

    <li>

           <bold>Drop held items</bold> - This isn't exactly damage, but

           it's an effect of taking damage.

    </li>

    <li>

           <bold>Fatigue</bold> - Every action the character undertakes

           tires him/her slightly. Do enough strenuous actions close

           enough together and the character will become fatigued.

    </li>

    <li>

           <bold>Knocked down</bold> - Again, it's not damage, but it

           a consequence of damage.

    </li>

    <li>

           <bold>Magic effect</bold> - Some sort of spell affects the

           character.

    </li>

    <li>

           <bold>Mana</bold> - The about of magical energy that the

           character can muster to case spells. It's very similar to

           fatigue.

    </li>

    <li>

           <bold>Unconscious</bold> - Of course, the character can

           become unconscious.

    </li>

    <li>

           <bold>Venom</bold> - Venom is treated more realistically

           in Circumreality than other RPGs. Instead of doing damage, venoms slowly

           (or quickly) seep into the character's bloodstream and

           cause mayham, potentially even killing the character.

    </li>

</ul>

 

 

 

 

 

<br/><section><p><bold><big>Bloodloss</big></bold></p></section>

 

<p>

    Characters have blood, and if they lose it through damage they

    will fall unconscious or die. The amount of blood that a

    character has lost is stored in <bold>pDamageBloodLoss</bold>.

    It ranges from 0.0 to 1.0. At 0.0 the character has all his/her

    blood, at 0.5 the character falls unconscious, and at 1.0 the

    character dies.

</p>

 

<p>

    Furthermore, the character's fatigue and mana

    can not be recovered to less than twice the bloodloss. Therefore,

    if the character has lost 0.25 of their blood (25%) then their

    fatigue can never get better than 0.5 (50%).

</p>

 

<p>

    To cause bloodloss in a character (and potentially unconsciousness

    or death), call <bold>DamageBloodLoss()</bold>.

</p>

 

 

 

 

 

 

 

<br/><section><p><bold><big>Dazed</big></bold></p></section>

 

<p>

    Blows to the head or chest, or perhaps even magic, can cause

    the character to become dazed. A dazed character is unable

    to act for a few seconds. To daze a character,

    call <bold>DamageDazed()</bold>.

</p>

 

 

 

 

 

<br/><section><p><bold><big>Death</big></bold></p></section>

 

<p>

    Characters can be killed outright by

    calling <bold>DamageDeath()</bold>. Usually death will

    result from some other sort of damage, such as bloodloss.

</p>

 

<p>

    A dead character falls to the ground and their body can

    be looted. After a few minutes, set by a global that you can

    modified, they're resurrected and moved to the last safe

    room they visited. If the character is a NPC the NPC and all

    contents it still holds are deleted.

</p>

 

<p>

    If you wish more severe penalties for death, such as permadeath

    or skill loss, you can modify the code for DamageDeath().

</p>

 

 

 

 

 

 

<br/><section><p><bold><big>Disease</big></bold></p></section>

 

<p>

    Diseases are more complex than the other systemic damages I've

    described so far. A Circumreality disease acts much more like a real diseases

    than other RPGs (which usually decrease the diseased character's

    strength for a few minutes and then the disease vanishes).

</p>

 

<p>

    A character catches a disease when <bold>DamageDisease()</bold> is

    called. If the character already has the disease then nothing

    happens. (I haven't implimented disease immunity though, so

    diseases can be caught more than once.)

</p>

 

<p>

    The disease begins with 0 "disease units", an intentionally

    vague value. Every second, the number of disease units is

    increased by an amount specified by the disease, multiplied

    by the virulence of the strain. Furthermore, if the character

    does any activity (that causes fatigue) then the disease will

    spread more rapidly.

</p>

 

<p>

    After a time (specified by the disease), the character's body

    will identify the diseas and then decrease the number of

    active disease units. When the number of disease units is

    knocked down to 0, the disease is eradicated.

</p>

 

<p>

    To create your own disease, you need to:

</p>

 

<ol>

    <li>

           <bold>Create a new object</bold> for your disease, called

           oDiseaseXXX, where XXX is the disease name.

    </li>

    <li>

           Set the disease's <bold>superclass to cDisease</bold>.

    </li>

    <li>

           Check the option to <bold>automatically create</bold> the

           disease object, although it will never be contained within

           a character.

    </li>

    <li>

           Fill in <bold>pNLPNounName</bold> and <bold>pNLPParseName</bold> to

           name the disease.

    </li>

    <li>

           Fill in <bold>pExamineGeneral</bold> with a line describing the

           disease's effects, that will be shown when the character

           asks for his/her status. If you don't wish to show anything,

           set this to NULL.

    </li>

    <li>

           Set <bold>pDiseaseRelease</bold> to the number of disease

           units created per second the disease is active.

    </li>

    <li>

           <bold>pDiseaseFatigue</bold> affects how much faster the

           disease reproduces when the character is active.

    </li>

    <li>

           <bold>pDiseaseStartResist</bold> is the number of seconds

           (on average) that it will take the character's body to

           begin resisting the disease.

    </li>

    <li>

           <bold>pDiseaseRecover</bold> is the number of disease

           units reduced per second once the character's body fights

           the disease.

    </li>

    <li>

           You should provide a <bold>VenomTimer()</bold> method.

           It is called when the disease is first introduced, and

           every few seconds thereafter until the disease is

           eradicated. The code in this function will control the

           effects of the disease; you can have the disease do

           any sort of damage by calling DamageXXX(), or even

           non-damage effects.

    </li>

</ol>

 

 

 

 

 

 

 

<br/><section><p><bold><big>Drop held items</big></bold></p></section>

 

<p>

    Calling <bold>DamageDropHeld()</bold> will cause the character

    to drop any held items in the given body part. This can be called

    as part of the damage. It is automatically called if the

    character receives significant damage to an arm, or is stunned

    in the arm.

</p>

 

 

 

 

<br/><section><p><bold><big>Fatigue</big></bold></p></section>

 

<p>

    Character's get tired, stored in a property

    called <bold>pDamageFatigue</bold>, which ranges from 0.0 to

    1.0. 0.0 means the character is fully rested, while 1.0

    is dead tired.

</p>

 

<p>

    You can cause fatigue damage by

    calling <bold>DamageFatigue()</bold>, although very few

    attacks will cause fatigue. (Some diseases might.) Furthermore,

    if too much blood is lost, fatigue will also be redurced.

</p>

 

<p>

    A small amount of fatigue is lost whenever the character

    performs an action. Fatigue is quickly regained, so

    the loss is seldom notieced However, if a character tries

    to perform too many high-fatigue actions in too short

    of a time, the character's fatigue will drop, performance

    will degrade, and eventually the character will be too tired

    to even move. (This frequently happens in combat.)

</p>

 

<p>

    To fatigue a character through an action,

    call <bold>ActionFatigue()</bold>.

</p>

 

 

 

 

 

 

<br/><section><p><bold><big>Knock down</big></bold></p></section>

 

<p>

    To knock a character to its feet, call <bold>DamageKnockDown()</bold>.

    Some attacks (such as shoves) will cause one character to

    fall down. If the character sustains enough damage to a leg

    it may also be knocked down.

</p>

 

 

 

 

 

 

 

 

<br/><section><p><bold><big>Magic effect</big></bold></p></section>

 

<p>

    Magic effect "damage" acts a lot like diseases. A magic effect

    is an object derived from cMagicEffect, although the exact

    mechanics of disease are not duplicated.

</p>

 

<p>

    To cause a magic effect, call <bold>DamageMagicEffect()</bold>.

</p>

 

<p>

    To create your own magic effect, you need to:

</p>

 

<ol>

    <li>

           <bold>Create a new object</bold> for your magic effect, called

           oMagicEffectXXX, where XXX is the magic effect name.

    </li>

    <li>

           Set the magic effect's <bold>superclass to cMagicEffect</bold>.

    </li>

    <li>

           Check the option to <bold>automatically create</bold> the

           magic effect object, although it will never be contained within

           a character.

    </li>

    <li>

           Fill in <bold>pNLPNounName</bold> and <bold>pNLPParseName</bold> to

           name the magic effect.

    </li>

    <li>

           Fill in <bold>pExamineGeneral</bold> with a line describing the

           magic effect's effects, that will be shown when the character

           asks for his/her status. If you don't wish to show anything,

           set this to NULL.

    </li>

    <li>

           Set <bold>pMagicEffectDuration</bold> to the average number

           of seconds that the character will be affected by the magic.

           Characters with higher oSkillMana will be affected less.

    </li>

    <li>

           You should provide a <bold>VenomTimer()</bold> method.

           It is called when the magic effect is first introduced, and

           every few seconds thereafter until the magic effect

           times out. The code in this function will control the

           effects of the magic effect; you can have the magic effect do

           any sort of damage by calling DamageXXX(), or even

           non-damage effects.

    </li>

</ol>

 

 

 

 

 

 

 

 

 

<br/><section><p><bold><big>Mana</big></bold></p></section>

 

<p>

    Mana works a lot like fatigue, except instead of it being

    physical damage, it's mental/magical. To decrease a character's

    mana, call <bold>DamageMana()</bold>.

</p>

 

 

 

 

 

 

 

<br/><section><p><bold><big>Unconscious</big></bold></p></section>

 

<p>

    To knock a character out, call <bold>DamageUnconscious()</bold>.

</p>

 

<p>

    Characters will automatically be knocked out if they lose too

    much blood or receive a severe blow to the head or chest.

</p>

 

 

 

 

 

 

 

 

 

<br/><section><p><bold><big>Venom</big></bold></p></section>

 

<p>

    Venoms (and poisons), like disease, are more complex in Circumreality than

    other RPGs. In Circumreality, a venom is somehow injected into the body (or

    in the case of alchohol, imbibed). A quantity of the venom

    sits in a "reserve" and slowly seeps into the character's

    bloodstream or lymph nodes. Doing physical activity increases

    the amount seeping in. At the same time, the character's live

    removes a fixed amount of venom from the bloodstream every

    second.

</p>

 

<p>

    To "inject" a venom into a character,

    call <bold>DamageVenom()</bold>.

</p>

 

<p>

    To create your own venom (or poison), you need to:

</p>

 

<ol>

    <li>

           <bold>Create a new object</bold> for your venom, called

           oVenomXXX, where XXX is the venom name.

    </li>

    <li>

           Set the venom's <bold>superclass to cVenom</bold>.

    </li>

    <li>

           Check the option to <bold>automatically create</bold> the

           venom object, although it will never be contained within

           a character.

    </li>

    <li>

           Fill in <bold>pNLPNounName</bold> and <bold>pNLPParseName</bold> to

           name the venom.

    </li>

    <li>

           Fill in <bold>pExamineGeneral</bold> with a line describing the

           venom's effects, that will be shown when the character

           asks for his/her status. If you don't wish to show anything,

           set this to NULL.

    </li>

    <li>

           Set <bold>pVenomDose</bold> to the number of liters of venom

           in one dose. (In the case of beer, this is 1.0 liter.)

    </li>

    <li>

           <bold>pVenomRelease</bold> is the percent of venom that seeps

           from the reserve into the character's bloodstream every second.

    </li>

    <li>

           <bold>pVenomFatigue</bold> lets you control how quickly the

           venom spreads if the character undertakes activity. (In real

           life, you have a much better chance of surviving a venemous

           snake bite if you remain calm than if you run around and

           quickly spread the venom through your system.)

    </li>

    <li>

           <bold>pVenomRecover</bold> is the number of liters of

           venom removed from the character's system every second.

           Characters with a higher oSkillEndurance will have venom

           removed more quickly.

    </li>

    <li>

           <bold>pVenomNonLethal</bold> controls whether NPCs with

           !pDamageCanBeAttacked are allowed to ingest the venom.

    </li>

    <li>

           You should provide a <bold>VenomTimer()</bold> method.

           It is called when the venom is first introduced, and

           every few seconds thereafter until the venom is

           eradicated. The code in this function will control the

           effects of the venom; you can have the venom do

           any sort of damage by calling DamageXXX(), or even

           non-damage effects.

    </li>

</ol>

 

 

 

 

 

 

<br/><section><p><bold><big>Localized damage</big></bold></p></section>

 

<p>

    In addition to systemic damage, each body part (based on

    oBodyPartXXX) can receive damage. The types of damage are:

</p>

 

<ul>

    <li>

           <bold>Bloodloss</bold> - Wounds within the body part can cause

           bloodloss, either external or internal.

    </li>

    <li>

           <bold>Bone breakage</bold> - Some weapons will break bones.

    </li>

    <li>

           <bold>Paralysis</bold> - Magic items and electrical damage

           will cause body paralysis.

    </li>

    <li>

           <bold>Severed</bold> - A body part can be severed.

    </li>

    <li>

           <bold>Tissue damage</bold> - Cuts into the flesh and blunt-weapon

           damage.

    </li>

</ul>

 

 

 

 

 

<br/><section><p><bold><big>Bloodloss</big></bold></p></section>

 

<p>

    To cause a wound that results in bloodloss,

    call <bold>BodyPartBloodLoss()</bold> in the body part object.

    Keep the bloodloss amount small; a number like 0.01 is a fairly

    large wound since it means that the character loses 1% of his

    blood every second, resulting in unconsiousness in 50 seconds,

    and death in 100 seoconds.

</p>

 

<p>

    There are two types of bloodloss, external and internal. Both

    result in calls to <bold>DamageBloodLoss()</bold> every few

    seconds. However, external bloodloss can be seen and can be

    bandaged. Internal bloodloss is invisible and cannot be

    healed by normal means; a character won't know he has internal

    bleeding until he falls unconscious.

</p>

 

<p>

    NOTE: The BodyPartXXX() functions for bloodloss, etc. are

    not usually called directly. They're most often a result of

    weapon damage, which is covered in the next tutorial.

</p>

 

 

 

 

<br/><section><p><bold><big>Bone breakage</big></bold></p></section>

 

<p>

    Calling <bold>BodyPartBoneBreak()</bold> will break the major

    bone in the body part, rendering it useless.

</p>

 

<p>

    Bones will not heal until they're set by someone skilled in

    setting bones. Then, they'll heal very slowly.

</p>

 

 

 

 

<br/><section><p><bold><big>Paralysis</big></bold></p></section>

 

<p>

    Calling <bold>BodyPartParalysis()</bold> temporarily paralyzes

    the body part. There are three types of paralysis: quick-recovery,

    medium-recovery, and slow-recovery. An electrical shock

    would cause quick-recovery paralysis, which disappears within

    a few seconds. A sting from a jellyfish might cause slow-recovery

    paralysis, taking a few minutes to completely recover.

</p>

 

<p>

    If a character's head or chest is paralyzed, the character

    may fall unconscious or even die.

</p>

 

 

 

<br/><section><p><bold><big>Severed</big></bold></p></section>

 

<p>

    Calling <bold>BodyPartSevered()</bold> severs the body part.

    Slashing weapons can cause a limb to be severed. If the head

    or torso of a character is severed, the character dies.

</p>

 

 

 

 

 

<br/><section><p><bold><big>Tissue damage</big></bold></p></section>

 

<p>

    The <bold>BodyPartTissueDamage()</bold> causes the body part's

    muscles to be damaged. (Or in the case of the head, the character's

    brain.) Enough damage and the body part will be useless until

    it heals. Significant tissue damage to the character's head or

    chest will knock it unconscious or kill it.

</p>

 

 

 

 

 

<br/><section><p><bold><big>Extensions to the cBodyPart class</big></bold></p></section>

 

<p>

    The cBodyPart class includes some extra properties and methods

    to handle body part damage. These are:

</p>

 

<ul>

    <li>

           <bold>pArmorXXX</bold> is a collection of armor values indicating

           how well the body part is self-armored against various sorts

           of damage. Humans have armor in their head (the skull) and torso

           (the rib cage). Some creatures, such as armadillos, have armor

           all around.

    </li>

    <li>

           <bold>pBodyPartDamgeImpaleCriticalHit</bold> is the chance,

           from 0.0 to 1.0, that a signficantly deep impaling hit will

           instantly kill the character. In humans, both the head and

           chest suffer this weakness, from hits to the brain or heart.

    </li>

    <li>

           Damage to a character is affected by the character's size (weight).

           However, some body parts are relatively smaller or larger

           than other body parts, and take more or less

           damage. <bold>pBodyPartDamageScale</bold> controls this; for

           a human, all the limbs are 1.0, while the head is 1.5 (since it's

           smaller), and the torso is 0.5 (since it's larger).

    </li>

    <li>

           <bold>pBodyPartDamageSlashCriticalHit</bold> is the chance that

           a sufficiently deep slash will cause a critical hit and kill

           the character instantly. In humans, the head and torso have

           a slight chance of instant death from a slash.

    </li>

    <li>

           <bold>pBodyPartIsHand</bold> should be set to true if the

           body part includes a hand or other gripping mechanism.

    </li>

    <li>

           <bold>pBodyPartIsLeg</bold> should be set to true if the body

           part acts like a leg. Significant damage to a leg could cause

           the character to be knocked to the ground.

    </li>

    <li>

           A value of true for <bold>pBodyPartIsLifeCritical</bold> will

           cause the character to fall unconscious or die if enough

           damage is done to the body part.

    </li>

</ul>

 

 

<p>

    Each body part object also suppots

    the <bold>BodyPartDamage()</bold> method. This interprets

    how damage from different types of weapons (such as slashing or

    impaling) affect the body part. The default code should work

    well enough for most creatures, but you may wish to rewrite

    this for particularly unusual creatures.

</p>

 

<p>

    As a general rule, you won't call BodyPartDamage() directly

    either. It is called, however, by existing methods when a

    character receives a hit from a weapon. This will be

    described in the next tutorial.

</p>

 

 

 

 

 

 

 

 

<br/><section><p><bold><big>Extensions to the cCharacter class</big></bold></p></section>

 

<p>

    The cCharacter class includes many properties and methods

    to handle the damage model. Many of them have already

    been described above. There are a few others of note:

</p>

 

<ul>

    <li>

           <bold>pBodyPartsInjuries</bold> is where the body-part specific

           injury lists are stored.

    </li>

    <li>

           If <bold>pDamageCanBeAttacked</bold> is set to TRUE, the character

           can be attacked. If it's FALSE, it can't be; you may wish

           to set this to FALSE for NPCs whose purpose is conversation or

           some other non-combative role.

    </li>

</ul>

 

04 Armor

Describes how to create armor objects and how armor works.

 

<section><p><bold><big>Armor</big></bold></p></section>

 

<p>

    In Circumreality, armor is an equippable object which, when worn,

    provides protection. Armor includes objects like chainmail

    shirts, greaves, and helmets. Armor does <bold>not</bold> include

    shields; these are considered to be weapons that parry.

</p>

 

 

 

<br/><section><p><bold><big>Creating an armor object/class</big></bold></p></section>

 

<p>

    To create a new piece of armor, such as a chainmail shirt:

</p>

 

<ol>

    <li>

           <bold>Create a new object/class</bold> as usual.

    </li>

    <li>

           Assign the <bold>cArmor</bold> superclass to it.

    </li>

    <li>

           Fill in <bold>pNLPNounName</bold> and <bold>pNLPParseName</bold>,

           as is typical for all objects.

    </li>

    <li>

           Because armor is usually heavy and bulky, make sure

           to fill in <bold>pWeightSelf</bold> and <bold>pVolumeSelf</bold>.

    </li>

    <li>

           Since armor usually only fits certain-sized people,

           make sure to fill

           in <bold>pEquipWeightMax</bold>, <bold>pEquipWeightMin</bold>, and

           maybe <bold>pEquipShape</bold>.

    </li>

    <li>

           Set <bold>pEquipLoc</bold> to the armor's location,

           such as <bold>[oBodyPartLocTorso, oBodyPartLocArmLeft,

           oBodyPartLocArmoRight]</bold> for a chainmail shirt.

    </li>

    <li>

           You need to specify how much damage the armor will absorb

           by setting various <bold>pArmorXXX</bold> properties,

           where XXX is

           substituted. <bold>pArmorBlunt</bold>, <bold>pArmorImpale</bold>,

           and <bold>pArmorSlash</bold> are the most common ones, but

           other damage types exist.

    </li>

    <li>

           You may also with to set <bold>pArmorAllBodyParts</bold> to

           TRUE if the armor is a magical medallion that protects the

           entire body, instead of just where it's worn.

    </li>

    <li>

           <bold>pArmorDamageAbsorb</bold> affects how much damage the

           armor can absorb before it's useless.

    </li>

    <li>

           Whenever armor is hit, it will transfer some damage "energy"

           into pushing damage that may knock the character over, even

           if the character isn't hurt. You may want to

           create magic armor that reduces the chances of

           knockdown due to this effect by

           setting <bold>pArmorPushScale</bold>.

    </li>

</ol>

 

 

 

 

<br/><section><p><bold><big>Armor gotchas</big></bold></p></section>

 

<p>

    Here are some things to watch out for with armor:

</p>

 

<ul>

    <li>

           Don't forget the <bold>pEquipWeightMin</bold> and <bold>pEquipWeightMax</bold>,

           because characters will be different sized.

    </li>

    <li>

           The armor's <bold>pWeightSelf</bold> and <bold>pVolumeSelf</bold> will

           be higher/lower for larger/smaller characters.

    </li>

    <li>

           The <bold>damage absorbed</bold> will be greater/smaller for larger/smaller

           suits of armor.

    </li>

    <li>

           Likewise, the amount of <bold>pArmorDamageAbsorb</bold> will

           vary with the size of the armor.

    </li>

    <li>

           You may wish to <bold>create a class</bold> for each type of armor, such

           as cArmorChainShirt, which looks

           at <bold>pEquipWeightSelf</bold>, and automatically calculated

           the above settings. That way, creating chainmail armor for halflings

           vs. armor for humans is only a matter of chaning one parameter,

           not four or five.

    </li>

    <li>

           You could go one step further and include some sort of

           steel-quality property that makes the armor more protective

           or longer-lasting.

    </li>

    <li>

           If you have non-human races that can't wear human-shaped

           armor you'll need to set <bold>pEquipShape</bold>.

    </li>

</ul>

 

 

 

 

<br/><section><p><bold><big>Armor on characters</big></bold></p></section>

 

<p>

    When a character is successfully attacked,

    the <bold>DamageArmor()</bold> call will eventually be

    called. (Usually after

    the <bold>DamageDoge()</bold> and <bold>DamageParry()</bold> calls.)

    This function looks through all the armor the character is wearing

    and reducing the damage accordingly. It, in turn,

    calls <bold>DamageNoArmor()</bold>, which then inflicts the damage

    onto the character's body parts.

</p>

05 Weapons and shields

Information about how to create your own weapon and shield objects.

 

<section><p><bold><big>Weapons and shields</big></bold></p></section>

 

<p>

    Weapons and shields are equippable objects. When the character

    attacks a weapon will be chosen to use. The character can

    also parry with a weapon and shield.

</p>

 

 

 

<br/><section><p><bold><big>Creating a weapon object/class</big></bold></p></section>

 

<p>

    To create a new weapon, such as a sword:

</p>

 

<ol>

    <li>

           <bold>Create a new object/class</bold> as usual.

    </li>

    <li>

           Assign the <bold>cWeapon</bold> superclass to it.

    </li>

    <li>

           Fill in <bold>pNLPNounName</bold> and <bold>pNLPParseName</bold>,

           as is typical for all objects.

    </li>

    <li>

           Make sure

           to fill in <bold>pWeightSelf</bold> and <bold>pVolumeSelf</bold>.

    </li>

    <li>

           Since weapons can only be wielded by people large enough (halflings

           don't do well with longswords), make sure to

           set <bold>pEquipWeightMax</bold>, <bold>pEquipWeightMin</bold>, and

           maybe <bold>pEquipShape</bold>.

    </li>

    <li>

           By default, a weapon's <bold>pEquipLoc</bold> is set

           to be single-handed. You can change this for two-handed weapons.

    </li>

    <li>

           You need to specify how much damage the weapon will do

           by setting <bold>pWeaponAttackDamage</bold>. This is list

           of the types of attacks that a weapon can do. For example: A

           sword can "swing" or "stab", swinging doing slashing damage

           and stabbing doing impaling. For each attack type, you need

           to supply the damage lists, with the carrier, non-carrier,

           and extra damage. For a complete description see the

           documentation for pWeaponAttackDamage.

    </li>

    <p>

           If one of the damage types is "throw" then the weapon can

           be thrown, like a dagger or an axe. A thrown weapon can't be

           parried (except by a shield), but it must be picked up by

           the attacker.

    </p>

    <li>

           You'll also need to set <bold>pWeaponHitSuccessDamage</bold> to

           control how much damage is done by barely-made hits. For swords,

           this number would be relatively high, such as 2.0, since a

           barely-made hit results in a slight gash, and only when a solid

           hit results is the full damage done. For arrows, a barely-made

           hit is nearly as good as a solid hit, so the value would be 0.5.

    </li>

    <li>

           Set <bold>pWeaponAttackTime</bold> to the number of seconds

           (optimum) between swings. Daggers will have fast attack

           times, of 4 or 5 seconds, while two handed-swords will be

           10-15 seconds.

    </li>

    <li>

           <bold>pWeaponLength</bold> specifies the length of the weapon.

           It allows characters with long weapons to attack taller characters,

           like giants.

    </li>

    <li>

           If it's a missle weapon, set <bold>pWeaponIsMissile</bold>. Only

           shields can parry missle weapons.

    </li>

    <li>

           <bold>pWeaponParry</bold> is the ability for the weapon to parry.

           Use 0.0 for long swords. Daggers will have a parry closer to -1 or -2,

           while shields will have a parry ability around 1.0.

           If the weapon cannot parry then set this to NULL.

    </li>

    <li>

           Set <bold>pWeaponSkillDamage</bold> to the skill (probably

           the strength attribute) that allows the weapon to do more damage.

    </li>

    <li>

           <bold>pWeaponSkillToHit</bold> should be filled with information

           about the weapon's skill, such as [oSkillSwordsmanship, 0.3]. The

           second entry in the list affects how many standard deviations

           are added to the character's chance of hitting, per skill level.

    </li>

    <li>

           If you want your weapon to be more accurate (or less accurate)

           than normal, then set <bold>pWeaponAccuracy</bold>.

    </li>

    <li>

           If your weapon wears out every time its used, wether or not

           it hits, such as a bow, then

           set <bold>pWeaponDamagedPerUse</bold> to a number like

           0.001, which allows 1000 uses out of it.

    </li>

</ol>

 

 

 

 

 

<br/><section><p><bold><big>Bows and arrows</big></bold></p></section>

 

<p>

    Bows and arrows (or guns and bullets) are a bit trickier since the

    bow is the weapon, but the arrows (or bullets) are used up for

    each attack.

</p>

 

<p>

    To create a bow:

</p>

 

<ol>

    <li>

           Base your object on <bold>cBow</bold> instead

           of cWeapon; cBow is, in turn, a sub-class of cWeapon.

    </li>

    <li>

           Set all the <bold>properties</bold> that you would for a weapon.

    </li>

    <li>

           Set <bold>pBowShape</bold> to a "shape" for the bow; only

           arrows of the same shape as the bow can be used. For

           example: You might use "longbow", "crossbow" or "10mm shell".

    </li>

    <li>

           The cBow class assumes that the bow uses 1 arrow per shot.

           However, if the "bow" is a squirt gun, and uses 0.1 liters

           of water per shot, then set <bold>pBowUnits</bold> to 0.1.

    </li>

    <li>

           If the bow stores its "arrows" internally, such as a gun,

           which contains bullets in a cartridge, then you should

           also base the bow off of <bold>cContainer</bold> and

           set <bold>pBowArrowLoc</bold> to TRUE so the bow code knows

           to look <bold>within</bold> the bow for arrows.

           Setting pBowArrowLoc to FALSE, or leaving it blank, will

           cause the bow to look for arrows held by the character.

    </li>

</ol>

 

<p>

    You must also create arrow objects:

</p>

 

<ol>

    <li>

           An arrow object is a subclass of <bold>cArrow</bold>.

    </li>

    <li>

           Fill in <bold>pNLPNounName, pNLPParseName,

           pVisual, and pExamineGeneral</bold> as

           usual.

    </li>

    <li>

           Make sure that <bold>pArrowShape</bold> is set to the same

           string as <bold>pBowShape</bold> in the bow.

    </li>

    <li>

           Arrow objects are ultimately collections. Therefore, you

           need to set <bold>pCollectionType</bold> so that arrows of

           the same type can be combined together.

    </li>

    <li>

           Likewise, set <bold>pQuantity</bold> to the number of arrows

           in the collection.

    </li>

</ol>

 

 

 

 

<br/><section><p><bold><big>Shields</big></bold></p></section>

 

<p>

    The cShield class, used for shields, is based off cWeapon.

    The only difference is that cSheild sets some properties

    appropriate for shields, such

    as <bold>pWeaponParryMissile</bold> so the shield can parry

    missile attacks, <bold>pWeaponParryAlways</bold> so

    the shiled is always parrying when

    equipped, <bold>pEquipOffhand</bold> so it's equipped in

    the left hand,

    and <bold>pWeaponSkillToHit</bold> to oSkillShield.

</p>

 

 

<br/><section><p><bold><big>Weapon gotchas</big></bold></p></section>

 

<p>

    Here are some things to watch out for with weapons:

</p>

 

<ul>

    <li>

           Don't forget the <bold>pEquipWeightMin</bold> and <bold>pEquipWeightMax</bold>,

           because characters will be different sized.

    </li>

    <li>

           You may wish to <bold>create a class</bold> for each type of weapon, such

           as cWeaponSword, which looks

           at <bold>pEquipWeightSelf</bold>, and automatically calculates

           the above settings and the damage.

           That way, creating sword for halflings

           vs. a sword for humans is only a matter of changing one parameter,

           not four or five.

    </li>

    <li>

           You could go one step further and include some sort of

           steel-quality property that makes the sword do more damage

           or longer-lasting.

    </li>

    <li>

           If you have non-human races that can't wield human

           weapons you'll need to set <bold>pEquipShape</bold>.

    </li>

</ul>

 

 

 

 

 

<br/><section><p><bold><big>Where weapons are invoked</big></bold></p></section>

 

<p>

    When a character is attacked, the <bold>DamageParry()</bold> call

    will be made. If the character can parry, it will be done

    here. If the parry fails, then <bold>DamageDodge()</bold> is called.

</p>

06 Combat

Describes the basics behind MIF's combat design.

 

<section><p><bold><big>Combat</big></bold></p></section>

 

<p>

    Most MUDs and virtual worlds have a turn-based combat system

    where each combatatant gets one attack evern 2-5 seconds.

    Because there aren't any choices for a player (or NPC) to make,

    players can press an "auto-attack" button and bash on their

    opponent until either the opponent dies, or the player decides

    to run away.

</p>

 

<p>

    Circumreality's combat is entirely different...

</p>

 

<ul>

    <li>

           An attack only occurs <bold>when the player presses the attack button</bold> or

           types in "attack ENEMY".

    </li>

    <li>

           A player can attack once every second if they wish, except that

           their <bold>attacks become ineffective when done that rapidly</bold>.

           This is handled by the <bold>pWeaponAttackTime</bold> parameter.

           Attacking too quickly will significantly decrease the chance of hitting and

           reduce the attacker's damage. Attacking longer will slightly

           increase the hit chance and damage, but at the expense of attacks.

           The <bold>ActionEffectiveness()</bold> call is made to determine

           how effective the attack it.

    </li>

    <li>

           Each attack also <bold>fatigues</bold> the attacker in

           a significant way, so a player wants to make their attacks count.

           A call to <bold>ActionFatigue()</bold> takes care of this.

           Note that it fatigues characters holding a lot of weight more

           than those lightly encumbered.

    </li>

    <li>

           However, if an attacker does not attack quickly enough, the

           defender will get in a counterattack. This will cause

           the attacker to <bold>parry or dodge</bold> causing

           a distraction, that then distrupts the attacker's attack.

           The function to disrupt a character

           is <bold>ActionDistraction()</bold>.

    </li>

    <li>

           Since parrying and dodging both use fatigue, and end up

           distracting the attacker, a character can

           control <bold>how much parrying and dodging</bold> their character does.

           If they have both on full, they aren't likely to get hit,

           but they won't be able to effectively attack, and they'll

           tire quickly. Conversely, a character in a full suit of armor

           can turn both off and just attack without worrying about

           the enemy's attacks getting through his armor.

    </li>

    <li>

           The attacker can also decide where to <bold>aim</bold>:

           high, middle, or low, and left, center, or right. If the

           attacker hits an undefended spot their chance of hitting

           increases.

    </li>

    <li>

           Aiming also (obviously) influences what body

           part is hit, allowing the attacker to use <bold>different strategies</bold>,

           like taking out the defender's legs, etc.

    </li>

    <li>

           Likewise, the defender can <bold>defend</bold> a location

           of their body.

    </li>

    <li>

           <bold>Different weapons do different types of damage</bold>: Swords

           causes bleeding, maces cause tissue and bone damage, etc.

    </li>

    <li>

           Some weapons have <bold>several attack types</bold>, such as

           swords being able to slash and stab, each producing a different

           type of damage.

    </li>

    <li>

           Some <bold>armor provides better protection</bold> against some types

           of attack. For example: Chainmail is great against slashing

           damage, but lousy against clubs or impaling.

    </li>

    <li>

           Characters can sustain <bold>several types of damage</bold>, including

           broken bones, severed limbs, tissue damage, and bleeding.

           Bleeding is very different from a traditional game because

           a wound will continue bleeding after it has been created, so

           a character hit by major sword wound won't fall unconscious right

           away, but may take 30 seconds before bloodloss overcomes him.

    </li>

</ul>

 

 

 

<br/><section><p><bold><big>Attack function calls</big></bold></p></section>

 

<p>

    When an attack is made, the following method calls are made:

</p>

 

<ol>

    <li>

           Attacker.<bold>CombatAttack()</bold> to initiate the attack.

    </li>

    <li>

           Attacker.<bold>CombatWeaponPrefGet()</bold> and

           Attacker.<bold>CombatWeaponPrefSet()</bold> to get and set the

           weapon that the attacker uses for combat.

    </li>

    <li>

           Defender.<bold>DamageParry()</bold> to allow the defender to

           parry the attack.

    </li>

    <li>

           Defender.<bold>DamageDodge()</bold> so the defender can dodge

           the attack.

    </li>

    <li>

           Defender.<bold>DamageArmor()</bold> is then called, letting

           the defender's armor absorb some of the damage.

    </li>

    <li>

           Defender.<bold>DamageNoArmor()</bold> is finally called with

           the damage that reaches the defender's flesh.

    </li>

    <li>

           cBodyPart.<bold>BodyPartDamage()</bold> receives the call

           with the damage that's specific to the body part.

    </li>

    <li>

           In turn, it calls <bold>BodyPartBloodLoss(),

           BodyPartBoneBreak(), BodyPartParalysis(), BodyPartSevered(),

           and BodyPartTissueDamage()</bold>.

    </li>

    <li>

           These may make calls back into the cCharacter,

           including <bold>DamageBloodLoss(), DamageDazed(),

           DamageDeath(), DamageDisease(), DamageDodge(),

           DamageDropHeld(), DamageFatigue(), DamageKnockDown(),

           DamageMagicEffect(), DamageMana(), DamageUnconscious(),

           and DamageVenom()</bold>.

    </li>

</ol>

07 Combat narration

Describes how players are informed about the results of an attack.

 

<section><p><bold><big>Combat narration</big></bold></p></section>

 

<p>

    Most MUDs display the results of combat in a predictable and

    fairly boring manner: "X hits Y for N damage." "Y dies."

    Unforuntately, this system does not work well for Circumreality because

    a) it's uninteresting, and b) Circumreality's combat is more complex

    than a typical MUD's.

</p>

 

<p>

    If Circumreality were to take the same apporach then a typical attack

    would produce a string like, "X attacks Y. Y defends high. X attacks

    middle. Y's parry fails. Y's dodge fails. X hits Y's legs.

    Y's leg breaks." This is a) uninteresting, and b) too longwinded.

</p>

 

<p>

    To combat this problem (pun intended), Circumreality first stores

    all of the combat information into a combat transcription (a

    cCombatTranscript) object, without speaking it out.

    It then passes the transcript object to CombatNarrate(), which

    investigages the transcript and finds a good way of wrapping

    up all the information into an interesting sentence, such

    as "X swings at Y's feet, avoiding Y's parry, hits Y's

    leg with a bone-crunching sound."

</p>

 

<p>

    For the most part, you don't need to worry about the specifics

    of the implimentation, unless you wish to add your own descriptions.

    For example: If you invented a special magical sword, you could

    have any attacks with it produce special descriptions. You could

    also write custom descriptions for monster attacks... "The octopuses

    tentacles grab X and squeeze."

</p>

 

 

 

 

<br/><section><p><bold><big>cCombatTranscript</big></bold></p></section>

 

<p>

    If you wish to write your own descriptions, the first thing

    you need to understand is how cCombatTranscript works.

</p>

 

<p>

    When an attack is initiated, a cCombatTranscript object is

    created. This object has no methods or properties, and is just

    a means of storing information. The object is passed through

    to all the combat functions and methods. If they have anything

    to "say", they will add a property to cCombatTranscript with

    the information instead of calling SpeakNarratorAll().

</p>

 

<p>

    Below are some of the basic properties that will be added.

    For a complete list, look at the cCombatTranscript defintion

    or help, or search through the code for "Transcript." references.

</p>

 

<ul>

    <li>

           <bold>pCombatLastAttacked</bold> - The character that was attacked.

    </li>

    <li>

           <bold>pCombatLastAttacker</bold> - The character doing the attacker.

    </li>

    <li>

           <bold>pCombatTranscriptType</bold> - This is "parry" if the

           defender parries, "dodge" if the defender dodges, or "hit"

           it the attack gets through.

    </li>

    <li>

           <bold>pCombatDodge</bold> - If the defender dodges, this

           is the how well the dodge went (in standard deviations). Thus,

           0.1 is barely dodged, while 2.0 is easily dodged.

    </li>

    <li>

           <bold>pCombatParry</bold> - If the defender parries, this

           is the how well the parry went (in standard deviations). Thus,

           0.1 is barely parries, while 2.0 is easily parried.

    </li>

    <li>

           <bold>pCombatTranscriptArmorAbsorb</bold> - If the defender's

           armor absorbs all the damage, this is the piece of armor that

           does it.

    </li>

    <li>

           <bold>pCombatTranscriptBoneBreak</bold> - If the defender's

           bone breaks as a result of the attack, this is the body

           part that breaks.

    </li>

    <li>

           <bold>pCombatTranscriptSevered</bold> - Like bone break,

           except this indicates that the limb was severed.

    </li>

    <li>

           <bold>pDamaged</bold> - The severity of the wounds inflicted on the

           defender. 4 for very large wounds, 3 for large, 2 for medium, 1 for minor,

           and left NULL/Undefined/0 for mere flesh wounds.

    </li>

    <li>

           <bold>pDamageDazed</bold> - Set to a number of the attack dazed the defender.

    </li>

    <li>

           <bold>pDamageDeath</bold> - Set to a number of the attack killed the defender.

    </li>

    <li>

           <bold>pDamageKnockDown</bold> - Set to a number of the defender was knocked down.

    </li>

    <li>

           <bold>pDamageUnconscious</bold> - Set to a number of the defender was

           knocked unconscious.

    </li>

    <li>

           <bold>pEquip</bold> - A list of equipment dropped by the defender,

           usually as a result of blows to arms.

    </li>

</ul>

 

 

<br/><section><p><bold><big>CombatNarrate()</big></bold></p></section>

 

<p>

    When an attack is finished and all its information is wrapped

    into a cCombatTranscript object, a call is made to

    CombatNarrate().

</p>

 

<p>

    CombatNarrate() first finds all the player characters in the

    room and determines what level of detail they wish to

    hear about. Usually, players will request a high level of detail

    for attacks they make and enemies attacking them, while a lower

    level of detail for combat ocurring with their friends.

    The levels are 0 for brief, 1 for short, 2 for normal, and 3

    for detailed.

</p>

 

<p>

    Next, CombatNarrate() finds all the narration objects

    in the system. These are based on <bold>cCombatNarrator</bold>, whose

    constructor automatically adds the object to gCombatNarrators.

</p>

 

<p>

    Each narrator object is passed in the transcript object and asked

    how relevent the narrator is to what just happened. (The method

    call is <bold>CombatNarratorWeight()</bold>. Most narrators

    will return a value of 1.0. However, if the narrator is designed

    for a special purpose, like attacks from giant octopi, then

    it tests the properies in cCombatTranscript. If the attack matches

    what the narrator is designed to describe, it will return a high

    value, like 10. If the narrator isn't designed for the type

    of attack, such as the octopus-narrator not handle sword attacks,

    then it will return 0.

</p>

 

<p>

    CombatNarrate() selects a narrator at random, taking into account

    the narrator's weight. It calls the

    the narrator's <bold>CombatNarratorNarrate()</bold> method.

    If the narrator can produce a decent narration for the event then

    it generates a string and sends it to SpeakNarratorList().

    If the narrator is unsuccessful, it returns FALSE and the

    CombatNarrate() function tries another narrator. Eventually,

    a narrator will be found that works, and the process stops.

</p>

 

 

 

<br/><section><p><bold><big>cCombatNarrator</big></bold></p></section>

 

<p>

    If you wish to write your own narrations,

    you need to <bold>create an object based on

    cCombatNarrator.</bold> I don't need to describe this in detail.

    Make sure that the narrator object is created as an object (as

    opposed to a class.)

</p>

 

<p>

    Each narrator object will have 10 to 1000 possible ways of

    "<bold>phrasing</bold>" the combat transcript. For example: "X attacks Y"

    could be phrased as "Y is attacked by X", "X hits Y",

    "X attacks Y with a sword", etc.

    Some phrasings will only be applicable to parries, dodges,

    or different attack types though.

</p>

 

<p>

    To create one of the phrases, <bold>add a private

    method</bold> to your new narrator object. The private method

    must accept a transcript object, list of actors who are to

    receive the narration, and a detail level. It should return

    a score, which indicates how accurately it was able to

    narrate the transcript. The easiest way to do this

    is return the value from CombatNarratorTieLooseEnds(),

    which calculates the score based on the properites used,

    as well as penalties for missed events. You might add a few

    extra points for an exceptionally good narration. If the phrasing

    code doesn't have any appropriate transcript it should return NULL.

</p>

 

<p>

    To see an example of narration code, look in oCombatNarratorBasic.

    You'll notice quite a few private methods, prefixed with "Short_",

    "Normal_", or "Detailed_". The "Short_" ones are used for

    a detail level of 0 or 1. The "Normal_" ones are used for

    a detail level of 2, while "Detailed_" is used for level 3.

</p>

 

<p>

    Some things to notice about the phrasing code:

</p>

 

<ul>

    <li>

           The method first checks to make sure that it can describe

           the information in the transcription.

           A method designed to describe succesful parries would return

           NULL if it were passed a "dodge" or "hit" value

           in pCombatTranscriptType.

    </li>

    <li>

           Detail level 0 (brief) and 1 (short) often use the same

           methods. They are virtually identical, except

           that level 0 won't display anything if the attack is parried

           or dodged.

    </li>

    <li>

           <bold>CombatNarratorTieLooseEnds()</bold> is called at the

           end of every phrasing method. This will speak out important

           information that was "missed" by the narrator code. For example:

           An attack might wound a defender's arm so badly that the defender

           drops his weapon. Since this is an unusual event, the narrator

           code may not pick up on it. The call to CombatNarratorTieLooseEnds()

           will speak the inforamtion though.

    </li>

    <p>

           To prevent CombatNarratorTieLooseEnds() from reiterating what

           the method just spoke, make sure to keep a record of the

           properties that were narrated and pass them to

           CombatNarratorTieLooseEnds().

           Thus, if the phrasing method told the players "Fred's mighty

           blow slays the orc.", ["pdamagedeath", "pcombattranscriptweapon"]

           would be passed. If you don't,

           CombatNarratorTieLooseEnds() will speak of "The orc dies."

    </p>

    <li>

           If the phrasing method is just being queried for a score,

           the ActorsList is NULL.

    </li>

</ul>

 

<p>

    You need to write a <bold>CombatNarratorPhrasings()</bold> method

    that returns a list of private methods that are appropriate for

    each detail level. oCombatNarratorBasic returns [Short_Parry,

    Short_Dodge, Short_Hit] for detail level 0 or 1. Higher levels of

    detail have many more ways to phrase a transcript.

</p>

 

<p>

    You may also wish to provide

    a <bold>CombatNarratorWeight()</bold> method so your narrator

    object is only used for certain types of attacks, or with

    specific weapons or monsters.

</p>

 

<p>

    That's it... Of course, you'll probably write a few hundred

    different phrasings, which can add up to a fair amount of

    work.

</p>

 

 

 

 

<br/><section><p><bold><big>Sound effects</big></bold></p></section>

 

<p>

    Sound effects for combat are also generated using the transcript

    object:

</p>

 

<ol>

    <li>

           <bold>CombatSoundEffectPlay()</bold> is passed a transcript

           object.

    </li>

    <li>

           It calls the <bold>CombatSoundEffect()</bold> method for the

           attacker, defender, the attacking weapon, parrying weapon,

           armor, and the body part attacked.

    </li>

    <li>

           Each <bold>CombatSoundEffect()</bold> object fills in a list of

           the sound effects it thinks should be used and returns that.

           For the most part, the methods just

           call <bold>CombatSoundEffectImpact()</bold> to determine the

           impact sound of weapon against armor or body part.

    </li>

    <li>

           <bold>CombatSoundEffectImpact()</bold> calls into the weapon

           and whatever object it hit. It uses

           the <bold>CombatSoundEffectMaterial()</bold> method

           to determine if the weapon is metalic, wood, etc., and also

           what the armor (or object that is hit) is like. Both

           of these value are used to pick a wave file to play, such

           as a sword clanking against steel armor, or sword against

           chainmail.

    </li>

    <li>

           <bold>CombatSoundEffectPlay()</bold> wraps up all the

           returned sounds and plays them on all the nearby-players computers.

    </li>

</ol>

08 Player vs. player combat

Enabling player vs. player combat.

 

<section><p><bold><big>Player vs. player combat</big></bold></p></section>

 

<p>

    Whenever a player character (or NPC) attacks or thinks about

    attacking, the function, <bold>CanAttackFunc()</bold> is called.

    This returns TRUE if the character can attack another,

    or FALSE if combat isn't allowed.

</p>

 

<p>

    CanAttackFunc() works by calling Attacker.CanAttack(),

    Defender.CanAttack(), and Room.CanAttack(). All the values

    are summed up (with the room's score being doubled). If

    the sum is zero or more then <bold>an attack is allowed</bold>.

    Otherwise, it isn't.

</p>

 

<p>

    PCs use the following rules in their CanAttack() method:

</p>

 

<ul>

    <li>

           If a PC tries to attack another <bold>member of his party</bold>,

           -100 is added, ensuring that party members can't be attacked.

    </li>

    <li>

           If the enemy is an <bold>NPC</bold> then +10 are added, allowing

           NPCs to be attacked by default.

    </li>

    <li>

           If the enemy (or a member of his party) has <bold>recently attacked the PC

           or the PC's party</bold>, then +10 points are added. The recently-attacked

           information is stored in <bold>pCombatLastAttackerList</bold>.

    </li>

</ul>

 

<p>

    NPCs use the following rules:

</p>

 

<ul>

    <li>

           If the NPC has <bold>pDamageCanBeAttacked</bold> set to FALSE, then

           -100 is added to the score, ensuring the NPC can't be attacked.

    </li>

    <li>

           If the enemy (or a member of his party)

           has <bold>recently attacked the NPC</bold>, then +10 points are added.

    </li>

</ul>

 

 

<p>

    Rooms use the following rules:

</p>

 

<ul>

    <li>

           If the room is a safe room, <bold>pRoomRessurectionLoc</bold> then

           the room returns -100, ensuring that combat won't take place.

    </li>

    <li>

           Otherwise, the room's <bold>pRoomCanAttack</bold> value is returned.

    </li>

</ul>

 

 

<p>

    To enable player vs. player combat, you can do one or more of the following:

</p>

 

<ol>

    <li>

           Set the room's <bold>pRoomCanAttack</bold> to +1.

    </li>

    <li>

           Create your own <bold>CanAttack()</bold> method for PCs that returns

           a high score (such as 10) when attacking each other. For example: You might allow

           orc PCs to attack elf PCs, but orc PCs cannot attack other orc PCs.

           Once one person has been attacked, they will be able to return the

           attack because their <bold>pCombatLastAttackerList</bold> will

           remember the attacker.

    </li>

</ol>

 

09 Resurrection

How to make a room that characters will be resurrected into.

 

<section><p><bold><big>Resurrection</big></bold></p></section>

 

<p>

    When a player character is killed, the character will be resurrected

    in about minute. To ensure that they don't get resurrected into the

    hands of their enemies, they are resurrected into the

    last <bold>"safe room"</bold> they had entered. This

    is stored in the character's <bold>pRoomRessurectionLoc</bold> property.

</p>

 

<p>

    To create a safe room, set the

    room's <bold>pRoomRessurectionLoc</bold> to TRUE. Whenever a PC enters

    the room, their safe room will be set to the room. The player will

    be notified of the new safe room too, assuming that their character's

    safe room has actually changed.

</p>

 

10 Object identification

Some objects can only be identified with the necessary skill.

 

<section><p><bold><big>Object identification</big></bold></p></section>

 

<p>

    Australia has an ancient family of plants known as "cycads".

    Cycads grow nuts on them that are poisonous. Most tourists don't

    know this, but the locals do. How is this implimented in Circumreality?

</p>

 

<p>

    Implimenting objects whose names change depending upon the

    character's skills is easy:

</p>

 

<ol>

    <li>

           Instead of making

           the <bold>pNLPNounName</bold> and <bold>pNLPParseName</bold> entries

           strings, make them lists. The first element of the list is the

           unskilled-name, such as "nut". The second entry is the name

           that the object would be known by to a skilled viewer,

           such as "poisonous nut". You can have more than two entry; in the

           case of cycad nuts, the third entry might be "a poisonous cycad nut".

           A fourth entry could contain the scientific name even.

    </li>

    <li>

           You can also modify <bold>pNLPNounAnimate, pNLPNounCount,

           pNLPNounGender, pNLPNounNoAutoArticle, pNLPNounNoAutoPlural,

           pNLPNounNoAutoPossessive,

           pNLPNounQuantity</bold>, and <bold>pNLPParseNameNoArticles</bold> to

           be lists too, although they don't have to be. (Being lazy,

           you'll usually leave the single values, and Circumreality will automatically

           use the same value for every level of identification.)

    </li>

    <li>

           Add a <bold>pNLPIdentifySkills</bold> property to the object. It

           is a list that contains information about what skills can be

           used to identify the object. There are multiple skills since,

           for example, a botanist would know what a cycad nut was, as

           well as someone with bush survival skills.

    </li>

    <p>

           Each skill has a sub-list. The first element of the sub-list

           is the skill object, followed by a number of (increasing) skill

           levels that correspond to the levels of detail in the object's

           name.

    </p>

    <p>

           For example: A cCycadNut object might have a pNLPIdentifySkills

           of [[oSkillBotany, 2, 4], [oSkillBushSurvival, 1, 2]].

           Characters with a skill level of 2 in botany, or 1 in bush

           survival would see the cycad nut described as "poisonous nut"

           instead of just "nut". A skill of 4 in botany or 2 in bush

           survival would see "poisonous cycad nut".

    </p>

</ol>

 

 

11 Loot

How to create spawned NPCs with equipment and loot.

 

<section><p><bold><big>Loot</big></bold></p></section>

 

<p>

    What good is a monster that isn't carrying some treasure?

    What good is an evil knight that tries to kick you to death?

</p>

 

<p>

    The Basic IF tutorial described how to have rooms automatically

    spawn NPCs using <bold>pSpawnClass</bold>. Unforuntately, these

    spawned creatures won't be carrying any treasure or weapons.

</p>

 

<p>

    To make a spawned creature carry treasure or weapons:

</p>

 

 

<ol>

    <li>

           Add <bold>pLootEquip</bold> to the NPC's object. This

           property allows you to control what weapons, armor, or other

           standard equipment the NPC will be created with.

           You can even randomly select the weapon or armor from a list,

           so not all NPCs of the class carry the same weapon.

           (The NPC's AI will automatically equip the weapon or armor

           as soon as it's created.) For details about the syntax

           for pLootEquip, see the property's help topic.

    </li>

    <li>

           <bold>pLootCreate</bold> lets you specify what treasure

           will be created on the NPC. Of course, the treasure selection

           is random. It is controlled by a value you set so that

           NPCs won't have too much treasure on them.

    </li>

    <li>

           If you wish treasure to appear when the NPC dies, such

           as a unicorn's horn being part of a unicorn's

           loot, you can provide a <bold>pLootCreateOnDeath</bold>.

           This acts just like pLootCreate except that the treasure is

           only created when the creature dies.

           (All NPCs support this, even those that <bold>aren't</bold> spawned

           by rooms.)

    </li>

</ol>

 

 

 

<br/><section><p><bold><big>Multiplayer loot appearing in rooms and containers</big></bold></p></section>

 

<p>

    There are cases where you might want the player to find an object when they enter a room,

    or when they open a container. For example: When a book is opened, a scrap of paper is inside

    the book that provides a clue.

</p>

 

<p>

    This is easy in a single-player game, since you just add an "oScrapOfPaper" and have it contained

    within the book.

</p>

 

<p>

    This won't work in a multiplayer game because only one player would ever get a chance to

    take the paper. All the others would find an empty book.

</p>

 

<p>

    There is a way to do (almost) the same thing in a multiplayer game:

</p>

 

<ol>

    <li>

           <bold>Create your room or container</bold> as usual.

    </li>

    <li>

           If you create a container, you must set <bold>pImmobile</bold> to TRUE. (You have to do this

           anyway in a multiplayer game since all take-able objects will either be taken by other

           players, or they'll be automatically cleaned up by the invisible room cleaner.)

    </li>

    <p>

           The container should have <bold>pOpen</bold> set to FALSE so it's closed. It should also

           be set to automatically close itself after being opened, with <bold>pOpenAutoClose</bold> so

           that if one player opens it, the object will automatically close and reset for the next player.

    </p>

    <li>

           Set <bold>pLootMultiplayer</bold> in the room or container to "cScrapOfPaper", or whatever

           class you have defined. You can also uses classes based on cMonster or cCharacter.

    </li>

</ol>

 

<p>

    That's it! The first time a player opens the book, a scrap of paper specifically for the

    player will be created. It won't be created subsequent times because a note is made

    in the character's <bold>pLootMultiplayerHistory</bold>.

</p>

12 Hidden objects

How to make hidden objects, like secret doors.

 

<section><p><bold><big>Hidden objects</big></bold></p></section>

 

<p>

    Making a hidden object is easy:

</p>

 

<ol>

    <li>

           Set the <bold>pHidden</bold> property of the object to TRUE.

    </li>

    <li>

           To control how difficult the object is to find,

           set the <bold>pSearchDifficulty</bold> property. This allows

           you to set the base difficulty, which can then be affected

           by skills (such as "Find hidden traps") that you might

           specify.

    </li>

    <p>

           Additonally, you can provide a bonus if the

           players search in the right spot; "Search in the bottom of the chest"

           might provide a bonus in finding a hidden panel

           at the bottom, while "Search the lid of the chest"

           would reduce the chance of finding the hidden panel.

           More on this later.

    </p>

    <li>

           You may wish to set <bold>pSearchDifficultyNoPerception</bold> if

           the character's oSkillPerception skill <bold>won't</bold> have any

           effect on searching.

    </li>

    <li>

           If you wish to change the string displayed when the hidden

           object is found the set <bold>pSearchFindString</bold>.

    </li>

    <li>

           You may wish secret doors to re-hide themselves a few

           minutes after they've been found.

           Set <bold>pSearchAutoHide</bold> to do this,

           as well as <bold>pSearchAutoHideString</bold> to change

           the description.

    </li>

    <li>

           You can change the string displayed while the player

           searches the object from "%1 searches %2." to

           "%1 rummages around the chest looking for something.", but

           changing <bold>pSearchString</bold> in the chest object.

    </li>

</ol>

 

 

 

<br/><section><p><bold><big>Spawned resources</big></bold></p></section>

 

<p>

    Since having resources, like plants and metal ores, spawn is common,

    the IF libraries contain special code for hidden resources. If

    you use this, all the spawned resources will

    be <bold>hidden</bold> by default.

</p>

 

<ol>

    <li>

           Set the room's <bold>pSpawnResourceClass</bold> to the number and

           type of resources that are spawned in the room.

    </li>

    <li>

           <bold>pSpawnResourceLocation</bold> is optionally set to the locations

           over which the resource is spawned.

    </li>

    <li>

           <bold>pSpawnResourceTime</bold> is optionally set to number of minutes

           between resource spawning. This defaults to an average of 10 minutes.

           See also <bold>pSpawnResourceTimeRange</bold> to limit the times.

    </li>

    <li>

           <bold>pSpawnResourceSearchDifficulty</bold> controls how difficult

           it is to find the resource, as well as what skills are needed.

           If not defined, then the difficult defaults to 0.

    </li>

    <li>

           <bold>pSpawnResourceSearchFindString</bold> describes the object being found.

           This can be left blank.

    </li>

</ol>

 

 

 

<br/><section><p><bold><big>Advanced hidden objects - Specific locations</big></bold></p></section>

 

<p>

    When the player types in the search command, they can (optionally)

    specify a location within the object to search. If a player

    is specific about the location, you should increase their chance

    of success if they state the right location, and decrease their

    chances if they state the wrong one.

</p>

 

<p>

    When the player types in their command, like "search the bottom

    of the chest", this is parsed down to "`search the bottom of

    CHESTOBJECT". The parser rules in rParserRPG also include

    some rules for parsing "`search [*1] [the] bottom [*2]" to

    "`search *1 `bottom *2". Thus, "search the bottom of

    the chest" becomes "`search `bottom of CHESTOBJECT".

</p>

 

<p>

    The Parse_Search() method in then discards the "of" word,

    and assumes that "`bottom" describes where the player is searching.

    The "`bottom" string is then passed into the search

    functions, and eventually into SearchForThis(), where

    the <bold>pSearchDifficulty</bold> property in the object

    indicates how searching the "`bottom" affects the success rate.

    See the docmentation of pSearchDifficulty for more information.

</p>

 

<p>

    You will probably need to add new parse rules for "the lid",

    "the sides", etc. of a chest, or whatever object you wish to

    have searched. To do this, provide

    a <bold>NLPRuleSetTempVerbs()</bold> property, or hardcode

    the rules for all your objects into a global rule set.

</p>

 

 

<br/><section><p><bold><big>Advanced hidden objects - Replacing methods</big></bold></p></section>

 

<p>

    You may also wish to replace some methods:

</p>

 

<ul>

    <li>

           <bold>SearchInsideThis()</bold> - If you add to this layered

           function you can ensure that searching around a forest will

           find random leaves and branches, searching around seashore

           will produce seashells, etc. Adding a SearchInsideThis()

           layer for trapped chests or doors will allow players to

           find the traps before springing them.

    </li>

    <li>

           <bold>SearchLookFor()</bold> - This is called when a specific

           object is searched for. If you rewrite this code, you can

           make objects that are hidden to some players while visible

           to others. You'll also need to

           replace <bold>IsHidden()</bold> and

           maybe <bold>SearchAutoHide()</bold>.

    </li>

</ul>

13 Currency

Gold pieces, silver pieces, copper pieces, and other currency.

 

<section><p><bold><big>Currency</big></bold></p></section>

 

<p>

    The default fantasy library comes with three

    currencies: <bold>cCurrencyGold, cCurrencySilver,

    and cCurrencyCopper</bold>. 1 gold is worth 16 silver.

    1 silver is worth 24 copper.

</p>

 

 

<br/><section><p><bold><big>Making your own currency</big></bold></p></section>

 

<p>

    If you wish to make your own currency:

</p>

 

<ol>

    <li>

           Create a class, cCurrencyMyOwn, and

           base it on <bold>cCurrency</bold>.

    </li>

    <li>

           Set <bold>pNLPNounName, pNLPParseName, and

           pExamineGeneral</bold> to describe the currency. You may wish

           to take a look at cCurrencyGold's pNLPNounName and pNLPParseName

           to ensure you handle all the parse/noun forms.

    </li>

    <li>

           Set <bold>pVisual</bold> to control what the currency

           looks like. Again, cCurrencyGold has a pVisual you may wish

           to look at.

    </li>

    <li>

           Set <bold>pCollectionType</bold> to a unique lower-case

           string for the currency, such as "myown".

    </li>

    <li>

           Modify <bold>gCurrencyList</bold> and add an entry for

           your currency, which might look like ["myown", "cCurrencyMyOwn", 1010].

           The 1010 is the value of the currency in terms inflation adjusted

           units, <bold>before</bold> gCurrencyInflation is taken into account.

    </li>

    <p>

           Make sure to place your currency entry in <bold>sorted order</bold>. Several

           functions assume that gCurrencyList is sorted from the highest

           value currencies to the lowest.

    </p>

    <p>

           If you <bold>don't wish to support gold pieces</bold>, for example, then make sure

           to remove that entry from gCurrencyList.

    </p>

</ol>

 

 

 

<br/><section><p><bold><big>Currency functions</big></bold></p></section>

 

 

<p>

    A number of handy currency functions are provided:

</p>

 

 

<ul>

    <li>

           <bold>CurrencyChargeActor()</bold> - This removes a given amount of

           money from a character and provides change. Or, it gives money to

           a character. It can also be used to provide change for a gold piece.

    </li>

    <li>

           <bold>CurrencyCoinToString()</bold> - Creates a string that

           can be displayed to the user, such as "2 gp, 3 sp" as in,

           "That mace will cost you 2 gp, 3 sp."

    </li>

    <li>

           <bold>CurrencyIAToCoin()</bold> - Converts from an inflation adjusted

           value to a list of coins. It automatically handles rounding.

    </li>

    <li>

           <bold>CurrencyOnActor()</bold> - Returns a list of all the currency

           objects held by the actor, or in the actor's <bold>unlocked</bold> containers.

    </li>

    <li>

           <bold>CurrencyOnActorToAI()</bold> - Accepts the

           results from CurrencyOnActor(), and calculates how much money the actor

           has in inflation adjusted units.

    </li>

    <li>

           <bold>CurrencyValue()</bold> - Given a lower-case currency name, like "gold", this

           returns the value in inflation adjusted units.

    </li>

</ul>

14 Repairable items

Making items that can be repaired.

 

<section><p><bold><big>Repairable items</big></bold></p></section>

 

<p>

    Items get damaged, stored in <bold>pDamaged</bold>. If

    you wish to make items that can be repaired by player characters or

    NPCs:

</p>

 

<ol>

    <li>

           Set <bold>pRepairSkill</bold> to indicate what skill is needed to

           repair the item, along with the skill level.

    </li>

    <li>

           Set <bold>pRepairTools</bold> to indicate the tools that must

           be handy for the item to be repaired. For example: The object

           might require a forge to be repaired.

    </li>

    <li>

           Set <bold>pRepairMaterials</bold> to include a list of materials

           that will be used up each time the item is repaired. The materials

           should be collections, since the amount of material depends upon

           how badly the item is damaged.

    </li>

</ol>

 

<p>

    Whenever a player or NPC repairs an item, it won't be 100% repaired.

    Characters with low skills will do a shoddy job repairing the item,

    while those with better skills will get closer to the 100% repaired

    mark.

</p>

 

<p>

    To account for this fact, <bold>pRepairBestCase</bold> will

    be modified higher each time, so that items can only be repaired

    so often before they're unrepairable.

</p>

15 Quests (cQuest)

How to create quests and quest objects.

 

<section><p><bold><big>Quests (cQuest)</big></bold></p></section>

 

<p>

    Quests are a ubiquitous CRPG feature that is supported by Circumreality.

    However, quests are handled slightly differently (and more powerfully)

    in Circumreality than most CRPGs.

</p>

 

<p>

    In Circumreality, a quest is really a hidden object that tags along in the

    character's inventory. It receives all the PerceiveXXX() messages that

    the character does, so it can monitor how many monsters were killed,

    how many blueberries were picked, etc.

</p>

 

<p>

    Unlike most CRPGs, Circumreality quests have a mind of their own. They are

    their own object, and can do what they wish, including but not

    limited to manipulating AIs. You can think of quests as "surrogate

    dungeon masters" whose job is to tag along with the player and make

    sure the game stays fun.

</p>

 

 

<br/><section><p><bold><big>Creating a quest</big></bold></p></section>

 

<p>

    To create a quest, such as one in which the player must slay ten orcs:

</p>

 

<ol>

    <li>

           Create a <bold>new class</bold>, such as "cQuestKillOrcs".

    </li>

    <li>

           Derive cQuestKillOrcs from <bold>cQuest</bold>.

    </li>

    <li>

           Fill in <bold>pNLPNounName</bold> and <bold>pNLPParseName</bold> for

           the name of the quest, "Bob wants you to slay 10 orcs".

    </li>

    <li>

           Fill in <bold>pExamineGeneral</bold> with a description of the quest.

           This description will be displayed when the player is offered the quest,

           as well as when the player checks out his quest's status.

    </li>

    <li>

           <bold>pQuestPlayers</bold> should be set the the recommended number

           of players to complete the quest. If you don't set this, it

           will default to 1, indicating that the quest is good for solo play.

    </li>

    <li>

           <bold>pQuestTasks</bold> must be filled in with a list of tasks for

           the player to complete.

    </li>

    <p>

           A final task of "Return to END-NPC." is automatically included.

           You can disable this task, or change the string, by

           changing <bold>pQuestTasksReturnToEnder</bold>.

    </p>

    <li>

           Set <bold>pQuestRewardEnum</bold> to the list of rewards offered to the

           player when the complete the quest. They will be shown the rewards when

           they accept the quest too. The <bold>default</bold> value for the

           property is no reward except gratitude.

    </li>

    <p>

           If any of your rewards are "special" rewards, that don't involve money

           or objects, then you'll need to write

           a <bold>QuestRewardSpecial()</bold> method.

    </p>

    <li>

           You might wish to write your own <bold>QuestCanShare()</bold> method

           to control whether players can share quests amongst themselves. The

           default behavior allows sharing in most cases.

    </li>

</ol>

 

<p>

    You might also want to do the following:

</p>

 

<ul>

    <li>

           <bold>pQuestGiverSpeak</bold> controls what the guest giver says when

           it hands out the quest.

    </li>

    <li>

           <bold>pQuestGiverGive</bold> will cause the quest giver to hand

           out items to the player, such as a letter that must be

           delivered. <bold>pQuestGiverSpeakAfterGive</bold> has the NPC speak

           a phrase after it has given the object(s) to the player.

    </li>

    <li>

           You may wish to set <bold>pQuestEnder</bold> to the NPC that the player

           needs to talk to in order to complete the quest. If you leave this

           as Undefined, then the player will need to return to the quest giver.

           If this is NULL, the player doesn't need to talk to anyone, and can

           complete the quest anywhere.

    </li>

    <li>

           <bold>pQuestEnderContextMenu</bold> is a string that's displayed in

           the pQuestEnder NPC's context menu that allows the player to finish

           the quest. The defaults to something like, "I'm done with the quest".

    </li>

    <p>

           The NPC's reply is controlled by <bold>pQuestEnderSpeakOnCompleted</bold>.

           It has a default.

    </p>

    <li>

           If the quest ender requies a set of objects from the player in order

           to complete the quests, then fill the objects

           in <bold>pQuestEnderTake</bold>. You may also wish to

           fill <bold>pQuestEnderSpeakBeforeTake</bold> with a phrase that the NPC

           speaks before it takes the objects, like "I'll be taking your foozle skins now."

    </li>

    <li>

           If the player completes the quest, starts talking to the pQuestEnder NPC,

           but doesn't mention pQuestEnderContextMenu, then the NPC will eventually

           bring up the fact that the player has completed

           the quest. Set <bold>pQuestEnterQuestionOnCompleted</bold> to the string.

           It defaults to something like, "So you completed the quest! What reward would

           you like?"

    </li>

    <li>

           When the player finally selects a reward, the quest is completed.

           The NPC will then speak <bold>pQuestSpeakOnCompletion</bold> with

           something like, "Thank you for completing the quest."

    </li>

    <li>

           To have the quest ender automatically offer another quest,

           set <bold>pQuestEnderOfferMoreQuests</bold> to TRUE, which is the

           default.

    </li>

    <li>

           If you want your quest to be hidden from the player,

           set <bold>pIsInvisible</bold> to TRUE. You wouldn't normally have an

           invisible quest since then players couldn't cancel it. However, if

           the quest is acting like an invisible dungeon master, this might

           be a good idea.

    </li>

    <li>

           Unlike most CRPGs, there are consequences to a player cancelling

           a quest. The most common one is that the quest giver will dislike

           and/or distrust the player for cancelling the quest.

           Adjust <bold>pQuestCancelledLike</bold> to control how much the

           quest giver's opinion of the player character is affected if the

           player cancels the quest.

    </li>

    <li>

           Change <bold>pQuestCancelCoolDown</bold> to control how long

           the player must wait before they can re-accept a cancelled quest.

    </li>

    <li>

           <bold>pQuestCompletedSpeak</bold> is spoken by the narrator when the

           quest is finished. If left blank, a default phrase is spoken.

    </li>

    <li>

           Conversely, <bold>pQuestCompletedLike</bold> increases the quest

           giver's like and trust when the quest is completed.

    </li>

    <li>

           <bold>pQuestCompletedReputation</bold> affects the PC's reputation

           when the quest is completed.

    </li>

    <li>

           By default, all quests will identify all "involved" NPCs to the

           player's characters so that when the player sees the NPC, he

           will have a name associated with it. This makes life easier for

           the player since they don't have to wander around the world

           asking NPCs their names. The NPCs are gotten from pQuestGiver,

           pQuestEnder, and any "MapToObject" listed in QuestTasks(). If

           you don't wish to automatically identify NPCs then

           set <bold>pQuestAutoIdentify</bold> to FALSE.

    </li>

    <li>

           If you provide <bold>pQuestKnowledgeAnecdote</bold> then the player

           can brag about his exploits on the quest to any NPC he may meet.

           This bit of knowledge can be used by the player when trading stories

           and rumors with NPCs.

    </li>

    <li>

           Setting <bold>pQuestFracture</bold> will cause

           PlayerCharacter.FractureAdd (pQuestFracture) to be called if

           the player completes the quest. Thus, a quest to slay the orcs

           will result in a happy village without any fires burning.

    </li>

    <li>

           <bold>pQuestAutoComplete</bold> lets the quest automatically

           end without having to see a NPC.

    </li>

    <li>

           If your quest object has properties that it needs to initialize

           when it's first created and assigned to a player, then

           write your own <bold>QuestInit()</bold>.

    </li>

    <li>

           If you quest needs special clean-up, then write your

           own <bold>QuestEnd()</bold>.

    </li>

    <li>

           You might write your own <bold>QuestTasks()</bold> method. This returns

           a list of all the tasks that the player must complete in the quest,

           as well as a percent-done for each task. The tasks are displayed

           in the quest information page. The default code looks at

           pQuestTasks and derives a list.

    </li>

    <li>

           If you use pQuestTasks and provide your own callbacks, you

           may need to call <bold>QuestTasksBoundary()</bold> from time to time

           to ensure that the player isn't allowed to "work ahead" on a task.

    </li>

    <li>

           Write your own <bold>QuestAIConversation()</bold> method to

           allow NPCs that are involved with the quest to interpret sentences

           spoken to them by the player. For example: If one task in your

           quest is to talk to Fred and get some information from him, you

           would provide a QuestAIConversation() method to return the

           string's that player could speak to Fred, such as "do you have [any]

           (information|info) for me".

    </li>

    <li>

           Write your own <bold>QuestAIQuestionAdd()</bold> method so

           that NPCs that are involved in the quest can bring up quest

           information in their conversations with the player. As in the

           example above, if the player doesn't mention that they are

           looking for information, Fred might eventually bring it up.

    </li>

    <li>

           If you use either QuestAIConversation() or QuestAIQuestionAdd() you

           will need to provide <bold>QuestAICallback()</bold> to have the

           NPC speak the phrase, and/or take actions.

    </li>

    <li>

           <bold>QuestAIShowOffer()</bold> and pQuestAIShow and pQuestAIOffer can

           be modified to control how pQuestEnder responds when shown or given one

           of the objects needed to complete the quest.

    </li>

    <li>

           You may wish to override <bold>QuestCanBeCompleted()</bold>, but

           you probably won't need to.

    </li>

    <li>

           <bold>QuestPercent()</bold> can be overridden, but as long as your

           QuestTasks() list is accurate, QuestPercent() will work fairly well.

    </li>

    <li>

           The default implimentation of <bold>QuestRewardEnum()</bold> returns

           pQuestRewardEnum. If you need more control than this then override

           QuestRewardEnum().

    </li>

    <li>

           <bold>pQuestCanCancel</bold> can be set to FALSE to prevent the quest from

           being cancelled, or you can write your own <bold>QuestCanBeCancelled()</bold> method.

    </li>

    <li>

           If you want to keep statistics about how long a quest took to complete

           or when players started/stopped the quest, then create statistics objects

           based on <bold>cStatisticsQuestDuration and/or cStatisticsQuestStartStop</bold> and

           reference the object(s) in <bold>pQuestStatistics</bold>.

    </li>

</ul>

 

 

<p>

    Quest objects also contain some useful properties that you don't need

    to initialize, but which might be handy during your coding:

</p>

 

<ul>

    <li>

           <bold>pIsQuest</bold> is set to TRUE, identifying the object

           as a quest.

    </li>

    <li>

           <bold>pQuestClass</bold> is the class name of the quest, "cQuestKillOrcs".

    </li>

    <li>

           <bold>pQuestGiver</bold> is the NPC that gave the player the quest.

    </li>

    <li>

           <bold>pQuestStartedPlayTime</bold> is the number of hours the player had

           played his characters when the quest was begun. You can use this

           for timed quests, or penalties for taking too long on a quest.

    </li>

    <li>

           <bold>pQuestStartedTime</bold> is the time from TimeGet() when the quest

           was begun.

    </li>

    <li>

           <bold>pQuestStoryline</bold> is the storyline object (based on cStoryline)

           that this quest is part of.

    </li>

</ul>

 

 

 

<br/><section><p><bold><big>Giving a quest to a player character</big></bold></p></section>

 

<p>

    There are two ways to give a quest to a player character:

</p>

 

<ol>

    <li>

           You can call <bold>cCharacter.QuestsAdd()</bold> to add the quest to the player's

           list of quests.

    </li>

    <p>

           While you're looking at QuestsAdd(), you might also wish to look

           at <bold>QuestsEnum(), QuestsFind(), QuestsRemove(),

           pQuestsCancelled and pQuestsCompleted</bold>.

    </p>

    <li>

           You figure out which NPC hands out the quest and modify some

           properties of the NPC so it will hand out the quest. See below.

    </li>

</ol>

 

<p>

    To easily have a NPC hand out a quest:

</p>

 

<ol>

    <li>

           Set <bold>pAIQuestHandout</bold> to a list of all the quests that the

           AI hands out, as well as information about requirements like previous

           quests that must have been completed.

    </li>

</ol>

 

<p>

    You can get a bit more control by modifying:

</p>

 

<ol>

    <li>

           <bold>pAIQuestQuestionAdd</bold> controls questions/statements the NPC

           might make based upon whether a quest has been completed or not. For

           example: If the player hadn't yet accepted the "kill 10 orcs" quest, the

           NPC (as well as every NPC in the village) might mention that orcs

           were troubling the village. When the player accepts the quest, a different

           line might be used. And finall, after the player finishes the quest,

           NPCs might occasionally thank the player for defeating the orcs. Likewise,

           if the player cancels the quest, the player might get some verbal abuse.

    </li>

    <li>

           <bold>AIQuestHandOut()</bold> gives you more control over the quests

           that a NPC hands out than pAIQuestHandOut.

    </li>

    <li>

           <bold>AIQuestHandOutUI()</bold> brings up a dialog that allows players

           to accept a quest that the NPC is providing. You probably <bold>won't</bold> need

           to override this, although you may wish to call it at some point.

    </li>

    <li>

           <bold>AIQuestQuestionAdd()</bold> provides even more control than

           pAIQuestQuestionAdd.

    </li>

</ol>

 

 

 

 

 

<br/><section><p><bold><big>Miscellaneous</big></bold></p></section>

 

<ul>

    <li>

           The <bold>QuestUI()</bold> function brings up a dialog for displaying

           the player's quests, or displaying information about a specific quest.

    </li>

    <li>

           <bold>cCharacter.QuestsCompletedOrCancelled()</bold> can be used to

           determine if a player character has completed (or cancelled) a quest.

    </li>

</ul>

 

 

 

<br/><section><p><bold><big>The rumor mill</big></bold></p></section>

 

<p>

    Quests can take advantage of a sub-system called "the rumor mill".

    The rumor mill causes NPCs to talk to one another about the quest, either

    spurring the player's interest in the quest (if they haven't accepted it),

    or talking about the quest's completion (assuming that the player has

    completed it).

</p>

 

<p>

    The rumor mill is a useful technique because:

</p>

 

<ol>

    <li>

           It make the quest feel important before players have heard about

           the quest or accepted it. For example: NPCs might say to one another,

           "I just saw another rat this morning!", "Yes, I know what you mean; They're everywhere!"

    </li>

    <li>

           Once players have completed the quest, NPCs will comment about

           their accomplishments. For example: "Thank god all the rats are gone!",

           "&lt;PC-Name&gt; certainly did a good job."

    </li>

</ol>

 

<p>

    To modify a quest to use the rumor mill:

</p>

 

<ol>

    <li>

           Create a conversation script based on <bold>cConvScriptRumorMill</bold> which

           described the "There's a plague of rats" conversation between NPCs. For more information,

           see the cConvScript tutorial.

    </li>

    <li>

           You may want to set <bold>pConvScriptAutoNPCs</bold> and <bold>pConvScriptAutoListenersExclude</bold> so

           that only certain NPCs will discuss the rumor, or so that NPCs won't talk about another NPC

           if that NPC is present.

    </li>

    <p>

           For example: If the quest is about spying on the strange activities of NPC X, then NPCs won't discuss

           the rumor while NPC X is in the room.

    </p>

    <li>

           In the quest object, set <bold>pQuestRumorMillConvScriptsNotStarted</bold> to the conversation

           script object that you just created.

    </li>

    <li>

           You might also wish to create several other conversation scripts and assign them

           to <bold>pQuestRumorMillConvScriptsActive, pQuestRumorMillConvScriptsCompleted,

           and pQuestRumorMillConvScriptsCancelled</bold>. These control what NPCs will say to one another

           once the quest has been accepted (but not completed), after it has been completed,

           or after it has been cancelled.

    </li>

    <li>

           If you want the quest's rumors to spread beyond the map where the quest is handed out,

           then set <bold>pQuestRumorMillMap</bold> to a list of maps where the rumors will be discussed.

           If you leave it blank, then the map where the NPC hands out the quest will be used.

    </li>

</ol>

 

<p>

    That's all you need to do.

</p>

 

<p>

    If you want to include rumors outside of the quest, then:

</p>

 

<ol>

    <li>

           Fill in <bold>pRumorMillSource</bold> in the NPC with a list of rumors.

    </li>

    <li>

           Optionally, fill in <bold>pRumorMillSourceMap</bold> with the map where the rumor

           will be spoken.

    </li>

    <li>

           Or, write your own <bold>RumorMillSource()</bold> method for the NPC.

    </li>

    <li>

           Or, write your own <bold>RumorMillConvScripts()</bold> method layer, assigned to the player

           characters. You can test for whatever conditions you like (such as different fractures) and

           add rumor-mill conversation scripts.

    </li>

</ol>

 

<p>

    You might also wish to change the following:

</p>

 

<ul>

    <li>

           <bold>gRumorMillLastTime</bold> and <bold>gRumorMillSpokenTime</bold> affect how often rumors

           are spoken, ensuring that players don't hear them too often.

    </li>

</ul>

 

 

16 Shape chaging

Magically changing a player character's shape... transforming them into frogs and whatnot.

 

<section><p><bold><big>Shape changing</big></bold></p></section>

 

<p>

    A fairly common fantasy device is to change a character's shape,

    such as turning them into forgs, mice, or bowls of petunias.

</p>

 

 

 

<br/><section><p><bold><big>Shape changing layer</big></bold></p></section>

 

<p>

    Before you even think about changing a character into a frog (or whatever),

    you need to create a special class, such as cShapeChangeFrog, that

    contains code and properties describing the essense of what makes a frog

    different from a man (or elf, dwarf, etc.)

</p>

 

<p>

    To create a shape changing class:

</p>

 

<ol>

    <li>

           <bold>Create a new class</bold> in the usual way. Make sure that

           it is <bold>not</bold> instantiated as an object. Give it a name,

           such as cShapeChangeFrog.

    </li>

    <li>

           Base the class off of <bold>cShapeChange</bold>.

    </li>

    <li>

           In cShapeChangeFrog, write code

           to <bold>override some important methods</bold>, such

           as NameRace(). See below for more information.

    </li>

    <li>

           Add some <bold>properties</bold> that you need to override,

           such as pBodyParts or pHeight. This is a <bold>tricky</bold> situation.

           See below for more details.

    </li>

</ol>

 

<p>

    When a character is changed into a frog, the cShapeChangeFrog class

    is <bold>layered</bold> on top of their character's existing class.

    (They layering is done using a call to LayerAdd().) This layer

    intercepts all method and property calls made to the character's object.

    If the layer happens to have the given method, that's called instead

    of the main object's method.

    Likewise, if the layer has a specific property, then that property

    is used instead of the character's property (kind of... see below).

</p>

 

<p>

    You may wish override one or more of the following methods in

    your cShapeChangeFrog class:

</p>

 

<ul>

    <li>

           <bold>EmoteQuery()</bold> - Use this to limit or change the emotes

           that the changed character can do. A frog may not be able to smile,

           but it can "ribbit". (You may also need to add an extra emote command, "ribbit",

           to allow the character to type the emote.)

    </li>

    <li>

           <bold>HeightEye()</bold> - Override this if the character can fly.

    </li>

    <li>

           <bold>NameRace()</bold> - Returns the race's name, such as "a frog".

           If you don't replace this, the character may still be known as "an elf" or

           "a dward".

    </li>

    <li>

           <bold>StealthAutoHide()</bold> - Causes the transformed character not

           to be noticed right away.

    </li>

    <li>

           <bold>VisualEmoteEnum()</bold> - Override this to control what visual

           emotes a character can make, perhaps eliminating "smile" and adding

           "throat sack full".

    </li>

    <li>

           <bold>VisualEmoteQuery()</bold> - Change this along with VisualEmoteEnum().

    </li>

    <li>

           <bold>VisualModifierEnum()</bold> - Change this along with VisualEmoteEnum().

           This method converts from an emote into morph shapes used in the character's

           3d model.

    </li>

</ul>

 

<p>

    You will also need to <bold>add several properties</bold> to cShapeChange frog.

    However, because this is a layered class added after the main object

    has been created, <bold>any values you type into the properties' edit

    fields will be ignored</bold>. The only way to actually change

    the value is to <bold>check the "Get/Set" checkbox next to the property and

    write your own Get() method</bold>.

</p>

 

<p>

    For example: You want your frog to be 1/10th of a meter tall.

    Normally, you'd set "pHeight" to 0.1. <bold>This won't work.</bold> Instead:

</p>

 

<ol>

    <li>

           <bold>Add</bold> the pHeight property as normal.

    </li>

    <li>

           <bold>Leave the pHeight edit field blank.</bold> It won't make any difference.

    </li>

    <li>

           <bold>Check the "Get/Set" checkbox.</bold>

    </li>

    <li>

           Edit the "Get" code by <bold>pressing the "Get" button</bold> next

           to the property edit field. The

           default code will be "return pHeight". <bold>Change this

           to "return 0.1;"</bold>.

    </li>

    <li>

           You can <bold>leave the "Set" code alone</bold>. There's no point changing

           it.

    </li>

</ol>

 

<p>

    You will need to write code for a number of properties. You may wish

    to change once or more of the following:

</p>

 

<ul>

    <li>

           <bold>pBodyParts</bold> - Controls what limbs your character has.

           Be aware that you may need to <bold>create special body parts</bold>

           for the shape change; frogs legs are different than human legs.

    </li>

    <li>

           <bold>pContainerItemsMax</bold> - The maximum number of items the character

           can hold.

    </li>

    <li>

           <bold>pContainerVolumeMax</bold> - The maximum volume of items the character

           can hold.

    </li>

    <li>

           <bold>pContainerWeightMax</bold> - The maximum weight of items the character

           can hold. This is affected by the character's strength attribute.

    </li>

    <li>

           <bold>pExamineGeneral</bold> - What other players hear if they examine

           the shape-changed character.

    </li>

    <li>

           <bold>pGender</bold> - If you want to do gender bending.

    </li>

    <li>

           <bold>pHeight</bold> - The character's height.

    </li>

    <li>

           <bold>pSkillsFemaleInnate</bold> and <bold>pSkillsMaleInnate</bold> - How male/female

           versions of the shape differ.

    </li>

    <li>

           <bold>pSkillsRacialInnate</bold> - Skills that the player automatically acquires

           when they're turned into this shape. This might, for example, include improvements

           to jumping as well as the ability to speak to other frogs.

    </li>

    <p>

           Important: Skills from pSkillsRacialLeanred are <bold>kept</bold> when a

           character is shape changed. That means that if oSkillElvish is part

           of pSkillsRacialLearned, then when the character is transformed into

           a frog, they will be able to speak Elvish. If you don't want this

           to happen, you could either add a negative Elvish skill to

           cShapeChangeFrog.pSkillRacialInnate, or you could make Elvish an

           innate skill instead of a learned one.

    </p>

    <li>

           <bold>pVoice</bold> - The character's voice; frogs might have a croaky voice.

    </li>

    <li>

           <bold>pVolumeSelf</bold> - How much volume the character uses.

    </li>

    <li>

           <bold>pWeightSelf</bold> - How much the character weighs.

    </li>

    <li>

           <bold>pDamageScreamBig, pDamageScreamHuge,

           pDamageScreamSmall, and pDamageScreamWince</bold> - What sounds the character

           makes when it's hit, probably a croak.

    </li>

    <li>

           <bold>pFOVRange360</bold> - The character's field of view.

    </li>

    <li>

           <bold>pHandedness</bold> - Force the character to be left/right handed.

    </li>

    <li>

           <bold>pThreeDSound</bold> - How the player hears sound when playing

           the shape-changed character.

    </li>

    <li>

           <bold>pVisualEffectAutoExposure</bold> - How well the shape-change sees at night.

    </li>

    <li>

           <bold>pVisualEffectColor</bold> - How colorful the world looks.

    </li>

    <li>

           <bold>pVisualEffectColorBlind</bold> - Make the shape-changed

           character color blind.

    </li>

    <li>

           <bold>pVisualEffectNearSighted</bold> - Make the shape-changed

           character near-sighted.

    </li>

    <li>

           <bold>pVisual</bold> - A 3D model of what the shape-changed character

           looks like.

    </li>

</ul>

 

 

 

<br/><section><p><bold><big>Changing a character's shape</big></bold></p></section>

 

<p>

    Once you've done all that, actually changing the character's shape is

    pretty easy:

</p>

 

<ol>

    <li>

           You can call <bold>ShapeChangeQuery()</bold> to get the character's

           current shape.

    </li>

    <li>

           Before changing the shape, find out what room the character is

           in and call <bold>vRoom.RoomShapeChange()</bold>. This is a safety

           check to ensure that characters won't be changed into shapes that

           aren't suitable for the room.

    </li>

    <p>

           For example: Part of the game might involve changing player characters

           into mice and letting them run through the walls of a building. While

           a player is in the wall, you don't (usually) want them to change back into a human.

           For one, how would you draw the inside of a wall from the perspective

           of a human?

    </p>

    <li>

           Call <bold>ShapeChange()</bold> to change the shape. The function

           takes parameters specifying what's to happen with the character's equipment.

    </li>

    <li>

           You should use <bold>SpeakNarratorAll()</bold> to inform everyone in the

           room that the shape change has occurred.

    </li>

</ol>

 

 

 

 

<br/><section><p><bold><big>Miscellaneous</big></bold></p></section>

 

<ul>

    <li>

           As alluded to above, if you want rooms, maps, regions, or zones where

           the player is forced into a specifc shape (or forced out of a shape),

           then write your own <bold>RoomShapeChange()</bold> for the room, map,

           region, or zone object.

    </li>

</ul>

17 Stealth

How sneaking about works.

 

<section><p><bold><big>Stealth</big></bold></p></section>

 

<p>

    Circumreality allows PCs and NPCs to sneak around through various commands

    like "Sneak DIRECTION", "Hide", and "Hide behind OBJECT". Sneaking

    allows characters to avoid enemies as well as listen in on conversations.

</p>

 

<p>

    For the most part, sneaking is auomatically handled by:

</p>

 

<ul>

    <li>

           <bold>oSkillStealth</bold> helps make characters more stealthy.

    </li>

    <li>

           <bold>oSkillPerception</bold> improves a character's ability to find

           hidden characters.

    </li>

    <li>

           <bold>IsThereLight()</bold> affects how dark it is, and how

           well characters can hide.

    </li>

    <li>

           If an object's <bold>pVolumeSelf</bold> is high enough (a few times

           higher than the character's volume), then the object can be hidden

           behind (or in).

    </li>

</ul>

 

 

 

 

<br/><section><p><bold><big>Objects/rooms that can hide in</big></bold></p></section>

 

<p>

    If you want to ensure that characters can hide behind an object, or hide in

    a room:

</p>

 

<ul>

    <li>

           Set <bold>pVolumeSelf</bold> for objects such as crates, dressers, or

           anything that a character might wish to hide behind or in.

    </li>

    <li>

           If pVolumeSelf isn't good enough, you can write your

           own <bold>StealthHidingPlaceQuality()</bold> method for the object. For example:

           If you wanted to allow players to hide under a pile of leaves,

           you'd need to write this.

    </li>

    <li>

           If you have a room that you want to make it easy to hide in, without

           having to hide behind an object, write your

           own <bold>StealthHidingPlaceQuality()</bold> for the room. For example: If the

           room is very smokey then hiding would be easy.

    </li>

</ul>

 

 

<br/><section><p><bold><big>How it works</big></bold></p></section>

 

<ol>

    <li>

           Whenever a character hides, their <bold>pStealthHidden</bold> property

           is set to indicate the object they're hiding in/behind, as well

           as how they're hiding.

    </li>

    <li>

           If any characters come into the room where a character is hiding, or

           if they search the room, <bold>StealthIsHiddenPerceived()</bold> is called.

           ActorMove() automatically handles this.

    </li>

    <li>

           If one character is hidden to another, then the

           hiding character's <bold>pStealthHiddenTo</bold> is expanded to

           include the character that can't see the hiding character.

    </li>

    <li>

           When a character is listed in pStealthHiddenTo, calls

           to <bold>IsHidden()</bold> will return TRUE for that character.

    </li>

    <li>

           Whenever the hidden character performs an

           action, <bold>ActionVoidStealth()</bold> is called, potentially

           exposing the hiding character. This, in turn,

           calls StealthIsHiddenPerceived().

    </li>

    <li>

           NPCs that perceive a character sneaking around,

           with <bold>PerceiveActorEnter(), PerceiveActorLeave(),

           and PerceiveInNewRoom()</bold> will mistrust the character.

    </li>

</ol>

 

 

<br/><section><p><bold><big>Miscellaneous</big></bold></p></section>

 

<ul>

    <li>

           If you want specific characters not to be noticed by

           other characters, write a <bold>StealthAutoHide()</bold> method

           for the character.

    </li>

</ul>

 

 

Documentation – Basic Artificial Intelligence Library

This library provides artificial intelligence routines for the interactive fiction title. It requires the Basic RPG library, and well as the Basic IF library to run.

 

01 cAI – Artificial intelligence

Information about what storylines are and how to create them.

 

<section><p><bold><big>cAI - Artificial intellgience</big></bold></p></section>

 

<p>

    Circumreality supports more complex artificial intelligence abilities

    than are found in most IF or MUD authoring kits.

</p>

 

<p>

    All of the AI programming is placed in this library, the

    artificial intelligence library. Most of the AI code is

    accessed from cAI, which is automatically included in

    the cCharacter class, so all characters will have AI.

</p>

 

<p>

    The Circumreality AI system includes the following features:

</p>

 

<ul>

    <li>

           <bold>Goals</bold> - The ability for an AI to have goals,

           and the use of a finite state machine to complete those

           goals. Goals might be as simple as the desire to another

           place in the world or to attack another character.

    </li>

    <li>

           <bold>Memories</bold> - AIs remember information about

           other characters. This information could include the

           character's names, how much they liked the last character,

           when they last saw them, etc.

    </li>

    <li>

           <bold>Conversations</bold> - AIs support primitive natural

           language parsing and conversation abilities. (Although it's

           primitive, it's infinitely more complex than conversations

           in most IF titles and MUDs though.)

    </li>

</ul>

 

<p>

    <bold>Important:</bold> If your game is a multiplayer game

    and you want a character (based off cCharacter, which

    is based off cAI) to <bold>remember players across reboots</bold>,

    then that <bold>character must also be based off

    cSaveToDatabase.</bold> However, characters that are based off

    cSaveToDatabase <bold>aren't</bold> allowed to enter private

    player instances, since those characters might end up trapped

    in the instance of one specific player, and not be available to

    other players.

</p>

02 cAIgoals – Goals

Information about what storylines are and how to create them.

 

<section><p><bold><big>cAIGoals - Goals and finite state machines</big></bold></p></section>

 

<p>

    When a game developer creates the code to handle the intelligence

    behind a computer-controlled character they usually use an algorithmic

    technique called a "Finite State Machine" (FSM). The FSM provides

    a semblence of intelligence, and allows the character to walk

    around the world, attack any enemies that come into range,

    and run away if it's wounded. FSMs <bold>don't</bold> handle conversations

    though.

</p>

 

<p>

    A FSM works by defining several "states" for a character's AI.

    A state is, essentially, a state of mind. Enemies in most

    first-person shooter games (FPS), have the following states:

</p>

 

<ul>

    <li>

           <bold>Partol</bold> - The NPC wanders around on a predefined

           route. If the NPC sees an enemy it switch to the "Attack"

           state.

    </li>

    <li>

           <bold>Attack</bold> - The NPC sees an enemy (such as the

           player) and attacks. If the player dies or manages to run

           away, the AI returns to the "Patrol" state. If the NPC

           is badly wounded the AI goes into the "Flee" state.

    </li>

    <li>

           <bold>Flee</bold> - The NPC runs away from the enemy

           and finds a space to hide. It then heals up over time, and

           returns to the "Patrol" state.

    </li>

</ul>

 

<p>

    Finite state machines work pretty well for FPS games because

    the player to NPC interactions are pretty simple: Kill or be

    killed.

</p>

 

<p>

    FSMs do start showing their weaknesses though. One major

    weakness in a simple FSM is that sub-FSMs are needed.

    For example: The "Attack" state really requires a number of

    sub-states, such as "Load weapon", "Fire", "Run towards enemy",

    "Take cover", etc. Each of these sub-states are a FSM by

    themselves. In effect, the ability for a FSM to have sub-FSMs

    (and sub-states) is like the C++ main() function being able

    to call other functions.

</p>

 

<p>

    The other weakness about FSMs is that they have a single

    track mind. If the programmer didn't program in a state

    transition, there's AI will never diverge from the state.

    This is especially noticable when FSMs are applied to

    AIs for IF titles or MUDs.

</p>

 

<p>

    You might want to design a character that wanders around

    the world. If the character sees something valuable it picks it

    up. If it happens by a merchant, it'll sell the object. If

    it's attacked, it will attack back. You can impliment this in

    a FSM, but the number of states and complexities of transitioning

    between states is huge. What you really need is a FSM for

    wandering around, a FSM for picking things up, a FSM for selling

    items to a merchant, and a FSM for attacking. However, a normal

    FSM system cannot handle <bold>four</bold> FSMs running at once.

</p>

 

<p>

    Circumreality's AI system can handle both sub-FSMs and multiple FSMs running

    at once.

</p>

 

<p>

    If you want to learn more about Finite State Machines, do a search

    on the Internet, or read some books about game AI.

</p>

 

 

<br/><section><p><bold><big>cAIGoal</big></bold></p></section>

 

<p>

    A finite state machine in Circumreality is called a "goal", because it

    does a lot more than a traditional FSM. Each goal is an object

    derived from cAIGoal. The object construct is used to group

    the code together rather to provide a common storage method.

    (In fact, you shouldn't store any information in your goal object

    because the same object will be used for several AIs, all running

    at the same time.)

</p>

 

<p>

    To create a goal object:

</p>

 

<ol>

    <li>

           <bold>Create a new object</bold> and name it "oAIGoalXXX", where

           XXX described what the goal does. A goal that wanders around

           randomly might be called "oAIGoalWanderAroundRandomly".

    </li>

    <li>

           Make it a sub-class of <bold>cAIGoal</bold>.

    </li>

    <li>

           Check the option to <bold>create as an object</bold> so

           it's an actual object, not just an abstract class.

    </li>

    <li>

           Write the <bold>AIGoalStart()</bold> method, as well as

           method for other states. See below.

    </li>

    <li>

           Optionally, write the <bold>AIGoalPriorityAdjust()</bold> method.

           See below.

    </li>

    <li>

           Optionally, you may want players to be able to ask the NPC

           what they're doing. To support this, provide

           a <bold>pAIGoalWhatDoingSpeak, pAIGoalWhatDoingLike,

           and pAIGoalWhatDoingImportance</bold> or

           write your own <bold>AIGoalWhatDoing()</bold>.

    </li>

</ol>

 

<p>

    Speaking in FSM terminology, AIGoalStart() is the first "state"

    in the FSM. Usually AIGoalStart() doesn't even act as a state

    in the traditional FSM meaning, but acts like an "AI planner"...

</p>

 

<p>

    When an AI creates a goal, it can send a parameter (or list

    with multiple parameters) to the goal just like a call to

    a function or method can be passed parameters. Many goal objects

    are written so the parameter doesn't contain enough information

    to determine what states need to be run. Instead, the first

    task of the goal object is to plan out the specifics of how

    to interpret the parameters.

</p>

 

<p>

    If this doesn't make sense (which it probably doesn't), then

    let me explain in English: An AI may have a goal of walking

    from its current room to the neighborhood bakery. The neighborhood

    bakery is a room in the world that isn't necessarily near

    the character's current position. When the oAIGoalMove goal

    is invoked by the AI, it will be passed in a parameter for

    the bakery's room, oRoomBakery. It's then up to the

    oAIGoalMove.AIGoalStart() method to figure out exactly how to

    get to the bakery, such as going north to oRoomStreetMain, then

    east to oRoomStreetOriely, then south into oRoomBakery. Only

    once it has a plan will AIGoalStart() pass control to another goal.

</p>

 

<p>

    Therefore, if you wanted to write oAIGoalMove, you'd need to

    write the AIGoalStart() method with the following pseudo-code:

</p>

 

<ol>

    <li>

           Figure out what room the AI is in.

    </li>

    <li>

           Figure out how to get from the character's current room to the new one.

    </li>

    <li>

           Remember this sequence of rooms so you don't need to recalculate it

           each time.

    </li>

    <li>

           Switch to the "walk to next room" state.

    </li>

</ol>

 

<p>

    You'll also need a "Walk to next room" state which has the following

    code:

</p>

 

<ol>

    <li>

           Look at the list of rooms to walk to. If it's empty then the AI

           has met it's goal, and should return TRUE to whatever began

           the goal.

    </li>

    <li>

           Wait 5 seconds (or whatever) so that the NPC appears to

           be waking at a normal pace. If the AI doesn't wait, the NPC

           will get to the bakery in mere microsends.

    </li>

    <li>

           Otherwise, look at the next room in the list and walk there.

    </li>

    <li>

           Remove the name from the list.

    </li>

</ol>

 

<p>

    Goal objects can do easily do this. Here's how:

</p>

 

<ol>

    <li>

           <bold>Write the code</bold> for AIGoalStart() as described

           above.

    </li>

    <p>

           AIGoalStart() is passed the character's object

           in the <bold>Actor</bold> parameter. This can be used to determine

           where the character is.

    </p>

    <p>

           The <bold>Parameter</bold> parameter of AIGoalStart() should

           expect the destination room to be passed as an object.

           With the character's current room and destination room known,

           AIGoalStart() can calculate the path needed using the A*

           algorithm. I won't get to that right now

    </p>

    <p>

           The AIGoalStart() method is passed a list in

           the <bold>Settings</bold> paramter. You can store any information

           in this list that you like. (It acts as a substitiute for member

           variables, which you <bold>cannot</bold> use to store information.

           Remember your oAIGoalMove object may be used by more than

           one NPC simultanously.

    </p>

    <p>

           Once the path has been written into the "Settings" list,

           AIGoalStart() should <bold>call AIGoalNext() and return

           the value</bold>. AIGoalNext() is provided by cAIGoal,

           and wraps up the input parameters into a list, that can

           be returned from AIGoalStart(), and is interpreted by

           AIGoalStart's caller. The code might look

           like <bold>return AIGoalNext (WalkToNextRoom, 5.0,

           NULL);</bold> WalkToNextRoom is the method that handle the

           "walk to next room" state. 5.0 is the number of seconds to

           delay before reaching that state. NULL is the parameter that

           will be passed into WalkToNextRoom, but it isn't needed.

    </p>

   

    <li>

           <bold>Create a private method called WalkToNextRoom</bold>. It

           should accept the same paramters as AIGoalNext().

    </li>

   

    <p>

           WalkToNextRoom() receives the same Settings list that was

           passed into AIGoalsStart(). It can look at the list

           and see what rooms it needs to move to. <bold>If the list

           is empty</bold> then WalkToNextRoom should <bold>return

           AIGoalNextFinished (TRUE);</bold> This informs the goal

           monitoring code in cAI that the goal is finished, and

           that is has been successfully completed (by returning TRUE).

    </p>

   

    <p>

           If there are still rooms on the list,

           call <bold>ActorMove()</bold> with the room, and <bold>remove

           the room from the Settings list</bold>. Then, <bold>return

           AIGoalNextRepeat(NULL)</bold> to repeat the same state (of

           WalkToNextRoom()), 5 seconds from now.

    </p>

</ol>

 

<p>

    If you want the NPC to walk to the bakery as soon as its

    created you can just modify <bold>pAIGoalsEmpty</bold> in the

    NPC object, and

    change it to <bold>[[oAIGoalMove, 0, oRoomBakery]]</bold>.

    If you start up your IF title, and can log on quickly enough,

    you'll see the NPC walking to the bakery. He will move to

    a new room every 5 seconds.

</p>

 

<p>

    The <bold>pAIGoalsEmpty</bold> parameter is used by cAI whenever

    it comes across an AI without any goals... which basically

    means when the object is first created. The list contains

    a sub-list for every goal the AI should start out with. Each

    sub-list starts with a goal object, followed by a priority, and

    then the parameter to pass into the goal's AIGoalStart() method.

    (I'll get to priority in a bit.)

</p>

 

 

<br/><section><p><bold><big>Sub-goals</big></bold></p></section>

 

<p>

    What happens if you want the NPC to wander from his starting

    room to the bakery, and then to the local pub, and then

    back to his starting location?

</p>

 

<p>

    You could modify the oAIGoalMove object that you wrote to

    handle all three locations, or you could use sub-goals, letting

    oAIGoalMove handle the sub-goal of moving from one spot to another.

</p>

 

<p>

    To create a goal that handles "Follow a path" that would

    move the NPC from A to B to C and back to A, just do the following:

</p>

 

<ol>

    <li>

           Create a new object, <bold>oAIGoalFollowAPath</bold> just like

           you created oAIGoalMove.

    </li>

    <li>

           Write the <bold>AIGoalStart()</bold> method for the goal.

    </li>

    <p>

           It should expect that the <bold>parameter</bold> passed into it is a list

           of rooms, such as [oRoomBakery, oRoomPub, oRoomStart].

           AIGoalStart() should <bold>remember this list in the Settings list</bold>.

           You can just do <bold>Settings.ListConcat(0, Parameter);</bold>,

           which will leave Settings with <bold>[0, oRoomBakery,

           oRoomPub, oRoomStart]</bold>. The first paremter, "0", is the next

           room in the list to go to.

    </p>

    <p>

           AIGoalStart() then should call <bold>return AIGoalNext

           (WalkedToRoom, 0, TRUE);</bold> This causes the WalkedToRoom

           goal to be run in 0-seconds and be passed in TRUE (which means

           that no error has occurred).

    </p>

   

    <li>

           Write a private method, <bold>WalkedToRoom</bold>, with the

           same parameters as AIGoalStart().

    </li>

   

    <p>

           If the <bold>Parameter != TRUE</bold> then there has

           been an error, and WalkedToRoom() should <bold>return

           AIGoalNextFinished (FALSE);</bold> This will stop the NPC's

           wandering around from room to room.

    </p>

   

    <p>

           Otherwise, the character should walk to the next room.

           Remember <bold>var vRoom = Settings[Settings[0]+1];</bold>,

           which is the next room on the list. Advance to the next

           room by calling <bold>Settings[0] = (Settings[0] + 1) %

           (Settings.ListNumber()-1);</bold>

    </p>

   

    <p>

           Then, to walk to vRoom, just <bold>return AIGoalNextSubGoal

           (WalkedToRoom, oAIGoalMove, 0, 0, vRoom);</bold> This

           tells the AI system to run the sub-goal, oAIGoalMove, which

           you wrote previously. oAIGoalMove is run with a priority of

           0 (which I'll explain later), and a delay of 0 seconds (why

           wait?). The parameter is vRoom, which is the next room to

           walk to. The first parameter, WalkedToRoom, is the state in

           this goal that will be called with oAIGoalMove reaches its

           goal. When oAIGoalMove.WalkToNextRoom() reaches its destination

           it will call AIGoalNextFinished(TRUE). The "TRUE" parameter

           will be passed into oAIGoalFollowAPath.WalkedToRoom(), so

           the "follow a path" goal knows that everything went okay.

    </p>

 

    <li>

           Modify <bold>pAIGoalsEmpty</bold> to

           be <bold>[[oAIGoalFollowAPath, 0, [oRoomBakery, oRoomPub,

           oRoomStart]]</bold>. Now, the NPC will walk between the

           bakery, pub, and start room and won't ever stop.

    </li> 

</ol>

 

 

<br/><section><p><bold><big>Running multiple goals at once</big></bold></p></section>

 

<p>

    What if you want your NPC to pick up any objects that it sees

    along its path?

</p>

 

<p>

    You could modify your oAIGoalMove code to also look for unclaimed

    objects and pick them up. However, since not all NPCs will

    be cleptomaniacs, you now need two goal objects, oAIGoalMove and

    oAIGoalMoveClepto. Plus, you'd need two "follow a path" objects,

    oAIGoalFollowAPath and oAIGoalFollowAPathClepto.

</p>

 

<p>

    There is an easier way...

</p>

 

<ol>

    <li>

           Create a new goal, <bold>oAIGoalPickUpAnything</bold> that

           ends up looking around and picking up objects that it sees.

           That's all it does. It will require a few methods just like

           my previous examples. I won't go into detail.

    </li>

    <li>

           Modify <bold>pAIGoalsEmpty</bold> to

           contain <bold>[[oAIGoalFollowAPath, 0, [oRoomBakery, oRoomPub,

           oRoomStart], [oAIGoalPickUpAnything, -10] ]</bold>. This

           new property tells the AI for the NPC object to create two goals,

           one of which is to wander around and the other is to pick

           up objects. The list element after oAIGoalPickUpAnything is

           -10, which is the priority. I didn't put a third element,

           the parameter, because pick up anything doesn't require

           any parameters.

    </li>

    <li>

           Modify your world to include <bold>some objects

           placed along the NPC's path</bold>.

    </li>

    <li>

           <bold>Start your IF</bold>.

    </li>

</ol>

 

<p>

    You'll be disappointed. The AI just wanders around the world

    and doesn't ever pick any of the objects up. That's because

    the priority score for oAIGoalPickUpAnything is less than

    the priority for oAIGoalFollowAPath. If you make the priority

    higher, you'll discover that the NPC picks up all the objects

    in the starting room, but doesn't ever move.

</p>

 

<p>

    The solution is to write a <bold>AIGoalPriorityAdjust()</bold> method

    for the oAIGoalPickUpAnything object.

</p>

 

<p>

    When the AI system realizes that more than one goal is active

    for a NPC, it uses the priority to determine which goal to run.

    However, you may want the priority to be dynamic, depending upon

    the character's location, surroundings, or health. The AI

    system automatically calls AIGoalPriorityAdjust() for each

    goal and adds

    the number it returns to the goal's score. Most goals don't

    bother with this function, but some need to.

</p>

 

<p>

    In the case of the cleptomaniac NPC, if oAIGoalPickUpAnything's

    AIGoalPriorityAdjust() returns 20 (or any number &gt;= 11), then

    the oAIGoalPickUpAnything goal will temporarily have a higher

    priority than oAIGoalFollowAPath. Therefore, write code in

    oAIGoalPickUpAnything.AIGoalPriorityAdjust() that <bold>checks

    the room for objects to pick up</bold>. If it finds any it

    can return 20. If it doesn't find any it will just return 0.

</p>

 

<p>

    If you make these changes the NPC will wander around its route

    and pick up any objects along the way.

</p>

 

<p>

    You can use multiple goals with dynamic priorities for lots of

    things:

</p>

 

<ul>

    <li>

           The "drink healing potion" goal would normally have a low

           priority that would increase if the character was wounded.

    </li>

    <li>

           The "run away" goal could likewise have a low priority that

           increases if the character is wounded.

    </li>

    <li>

           A "find the store and eat goal" could increase its priority

           based upon the hunger of the character.

    </li>

    <li>

           Etc.

    </li>

</ul>

 

 

 

<br/><section><p><bold><big>Temporarily suspending goals</big></bold></p></section>

 

<p>

    An NPC wandering around the world and picking up anything

    can also be achieved by

    using <bold>AIGoalNextSuspend()</bold>. A goal which temporarily

    suspends itself waits a given amount of time before waking up

    and doing processing. The "pick up anything" goal could wake up

    every 5-10 seconds, and see if any objects were around. If there

    weren't any it would go back to sleep, suspending itself for

    another 5-10 seconds. If it found an object it would initiate

    the <bold>oAIGoalObjectGetDrop</bold> goal to pick up it up.

</p>

 

 

 

<br/><section><p><bold><big>Programmatically adding new goals</big></bold></p></section>

 

<p>

    So far I've been using <bold>pAIGoalsEmpty</bold> to add goals

    to the AI's list of goals. If you wish to have some other

    code add a new goal, you can call <bold>oNPC.AIGoalAdd()</bold> to

    add one or more goals to the list.

</p>

 

<p>

    Remember, that when a goal is added the highest priority ones

    will run first, while lower priority goals will wait until

    they're highest priority. (Unless, of course, the goal supports

    AIGoalPriorityAdjust()).

</p>

 

<p>

    You can use this to your advantage. For example: A NPC has the

    oAIGoalFollowAPath goal running. However, if another character

    speaks to the NPC, then your code to add the goal

    oAIGoalHavingConversation with a <bold>higher</bold> priority

    than oAIGoalFollowAPath. This would cause the NPC to stop

    walking and talk to whomever has asked the question. If the NPC

    weren't asked a question for 20 seconds, or if the other

    character walks away, the oAIGoalHavingConversation goal could

    exit by returning AIGoalNextFinished(), and the NPC would

    procede to follow its path.

</p>

 

<p>

    cCharacter objects, as well as any other object with cAI as a superclass

    also supports the following methods:

</p>

 

<ul>

    <li>

           <bold>AIGoalAdd()</bold> - As mentioned above. Add a goal to

           the AI's list of goals.

    </li>

    <li>

           <bold>AIGoalFind()</bold> - Find a goal in the AI's list.

    </li>

    <li>

           <bold>AIGoalNumber()</bold> - Return the number of goals.

    </li>

    <li>

           <bold>AIGoalOverride()</bold> - This is a method that you write

           yourself. It lets you create a custom personality for NPCs, so

           that they use different goals than the norm. For example: If you

           want a character that uses different attack aiming tactics than normal,

           you would have the AIGoalOverride() look for

           references to the default aim handler, oAIGoalCombatAimChoose,

           and return your own goal, such as oAIGoalCombatMYAimChoose.

    </li>

    <li>

           <bold>AIGoalQuery()</bold> - Get information about a goal,

           such as its callback, timing, or whether it's suspeneded until

           a sub-goal finishes.

    </li>

    <li>

           <bold>AIGoalRemove()</bold> - Remove a goal from the AI's list.

    </li>

</ul>

 

 

 

 

<br/><section><p><bold><big>Summary</big></bold></p></section>

 

<p>

    Goals are the basic mechanism for getting a NPC to "do stuff"

    like walk around, fight, etc. You can have goals run sub-goals,

    as well as running several goals at once. By combinging sub-goals

    and multiple goals (potentially with dynamic priorities) you can

    create a wide variety of behaviours for your AIs.

</p>

 

<p>

    For a complete lists of pre-programmed

    goals, look for objects with the "oAIGoal" prefix.

</p>

03 Movement goals

Provides an overview of goals that can be used to move NPCs around.

 

<section><p><bold><big>Movement goals</big></bold></p></section>

 

<p>

    The AI library includes a number of goals designed for

    NPC movement. Here's a quick overview: (For more inforamtion about

    the goals look in the documentation specific to the goal

    object.)

</p>

 

<ul>

    <li>

           <bold>oAIGoalMoveFollowCircuit</bold> - With this goal, you

           provide a set of destinations, such as Room A, Room F, and

           Room Q, and the AI automatically wanders from Room A to Room F,

           to Room Q, and then back to Room A. The path between A and F,

           F and Q, and Q and A is automatically calculated.

           This goal use useful for <bold>guards</bold> and other NPCs

           with set movments.

    </li>

    <li>

           <bold>oAIGoalMoveFollowObject</bold> - NPCs using this goal

           will follow an object around, wherever it is in the world.

           It can be used to <bold>have monsters chase after fleeing

           characters, for pets, or to produce monsters that

           stalk characters</bold>.

    </li>

    <li>

           <bold>oAIGoalMoveFollowPath</bold> - This goal causes an AI

           to follow an exact path between rooms. All rooms must be connected.

           You probably won't use it; instead, look at oAIGoalMoveFollowCircuit

           or oAIGoalMoveToRoom.

    </li>

    <li>

           <bold>oAIGoalMoveFollowPersonalPC</bold> - NPCs follows

           whatever PC is assigned to its pPersonalNPCFor property.

    </li>

    <li>

           <bold>oAIGoalMoveRandomCircuit</bold> - This is like

           oAIGoalMoveFollowCircuit except that the immediate destination

           is randomly chosen from any of the rooms in the list.

           Use this for <bold>unpredictable guards</bold> who check out

           rooms, but not in any specific order.

    </li>

    <li>

           <bold>oAIGoalMoveLeadObject</bold> - You can use this goal,

           in conjuction with oAIGoalMoveToRoom, to have the NPC lead

           a character along a path. If the character falls behind or

           wanders off the path the NPC can (optionally) chase the

           character down and ask the character to follow.

    </li>

    <li>

           <bold>oAIGoalMoveToAdjacentRoom</bold> - This goal is a sub-goal

           of all the movement goals. It causes a NPC to move to a

           room that's adjacent to the one it's in. In the process it

           will open (and unlock doors), and get up if it's knocked down.

    </li>

    <li>

           <bold>oAIGoalMoveToObject</bold> - The AI will located an

           object (by cheating) and head towards the room that contains

           the object, like oAIGoalMoveToRoom. However, if the object

           moves in the meantime, the object will identify the move and

           adjust its course. Use this for <bold>panicked villagers

           looking to find the town's guards</bold>.

    </li>

    <li>

           <bold>oAIGoalMoveToOrigin</bold> - Invoking this goal will

           cause the NPC to return to wherever it was created. This

           is a useful goal for getting <bold>fleeing NPCs to return

           to where they belong</bold>.

    </li>

    <li>

           <bold>oAIGoalMoveToRoom</bold> - The AI will walk to the

           specified room.

    </li>

    <li>

           <bold>oAIGoalMoveWanderAroundMap</bold> - Using this, the NPC

           will wandering around any room it can get to in a map (or

           maps). Use this for <bold>wandering animals and monsters</bold> that

           have no fixed location.

    </li>

    <li>

           <bold>oAIGoalMoveWanderNearby</bold> - AI's with this goal will

           wander around the starting room, but never very far from

           it. You can use it for <bold>spawned monsters in dungeons</bold> that

           should basically stick to their spawning point.

    </li>

    <li>

           <bold>oAIGoalWaitForObject</bold> - Causes the AI to wait around

           for an object to appear in the room. This is useful for "waiting

           for the cows to come home" or cGuardExitExtra - waiting to make sure

           the exit isn't left unguarded.

    </li>

</ul>

 

 

<p>

    Some movement-related goals are used by the movement goals:

</p>

 

<ul>

    <li>

           <bold>oAIGoalObjectLock</bold> - The AI will lock or unlock

           an object, such as a door or chest.

    </li>

    <li>

           <bold>oAIGoalObjectOpen</bold> - This causes the NPC to open

           or close a door or container. Optionally, the NPC can unlock

           or lock the container.

    </li>

    <li>

           <bold>oAIGoalStandUpSitDown</bold> - Causes the NPC to stand

           up or sit down.

    </li>

    <li>

           <bold>oAIGoalEmote</bold> - Causes a NPC to emote.

    </li>

</ul>

 

04 Combat AI goals

Describes how the combat AI works.

 

<section><p><bold><big>Combat AI goals</big></bold></p></section>

 

<p>

    The AI that handles combat is a bit tricky, so this section

    will be a tad long...

</p>

 

 

<br/><section><p><bold><big>Enemies list</big></bold></p></section>

 

<p>

    Every AI (derived from cAI) remembers a list of dangerous enemies,

    as well as friends that will help in combat. (This list is different

    than the like/dislike list also supplied for AI's.)

</p>

 

<p>

    Characters that a NPC meets are rated from 10.0 to -10.0 on the

    enemies list. Values between 10.0 and 1.0 mean that the character

    is an enemy that will attack the NPC. .999 and -.999 is a neutral

    character whose intentions are unknown. -1.0 to -10.0 is a trusted

    friend that will help defend the NPC in combat.

</p>

 

<p>

    When a NPC first encounters a character, a call

    to <bold>AIEnemiesListGuess()</bold> is called to see if the

    NPC considers the character an enemy. The default behavior

    for this function is to:

</p>

 

<ol>

    <li>

           If the AI has <bold>pAIEnemyOfAllPCs</bold> then the AI will

           automatically be an enemy of any character controlled by

           a player. This is a good setting for a monster.

    </li>

    <li>

           If the AI was <bold>spawned by the same room</bold> as another AI then

           it will be a friend with the other AI.

    </li>

    <li>

           If the AI is the same <bold>class</bold> as the other AI

           it's an automatic friend too. Thus, if a number of tigers

           are spawned in different rooms of the wilderness, they

           will act as friends.

    </li>

</ol>

 

<p>

    You can provide your own AIEnemiesListGuess() function for

    an AI.

</p>

 

 

<p>

    The enemies rating is affected by what the AI sees:

</p>

 

<ol>

    <li>

           When a <bold>NPC is attacked</bold>, the attacker is almost invariably added

           to the NPC's enemies list.

           See <bold>cAI.PerceiveAttack</bold> for the code.

    </li>

    <li>

           When the <bold>NPC see a friend attacked</bold>,

           the attacker is likewise added (but not always).

           See <bold>cAI.PerceiveAttack</bold> for the code.

    </li>

    <li>

           If a <bold>character

           attacks a NPC's enemy</bold>, they are (usually) made a friend.

           See <bold>cAI.PerceiveAttack</bold> for the code.

    </li>

    <li>

           If an AI starts yelling out for help

           using <bold>AICombatCallForHelp()</bold>, the

           code in <bold>cAI.PerceiveSpeak()</bold> may cause any

           AI's that hear the yell to become enemies of the attackers.

    </li>

</ol>

 

<p>

    You can programatically set and read the enemies' list values

    using the following methods:

</p>

 

<ul>

    <li>

           <bold>AIEnemiesCalculateTheOdds()</bold> - Used by AIs to

           determine if they're likely to win a battle of if their

           enemies are likely to win. It's based on AIEnemiesListUpdate().

           You probably won't need to use this method directly.

    </li>

    <li>

           <bold>AIEnemiesListGet()</bold> - Returns the enemies rating,

           from 10 to -10, for a character. If the character has never

           been met it returns NULL.

    </li>

    <li>

           <bold>AIEnemiesListGuess()</bold> - Call this if no rating

           is set. It will return a rating to begin with, or Undefined

           if there's no opinion whatsoever. Calling ToNumber (

           AIEnemiesListGuess()) will always return a number, since

           Undefined will be converted to 0.

    </li>

    <li>

           <bold>AIEnemiesListSet()</bold> - Sets a new rating for the

           enemy or friend.

    </li>

    <li>

           <bold>AIEnemiesListUpdate()</bold> - Finds all the characters

           in a room and checks their enemies' list score. If it's

           unknown, then it calls AIEnemiesListGuess() to fill them in.

           This method can optionally return a list of enemies, friends,

           or both enemies and friends.

    </li>

</ul>

 

 

 

 

<br/><section><p><bold><big>Perceiving a possible conflict</big></bold></p></section>

 

<p>

    AI's check for enemies in the room when:

</p>

 

<ul>

    <li>

           The AI <bold>walks into a new room</bold>.

           This code is on <bold>cAI.PerceiveInNewRoom().</bold>

    </li>

    <li>

           When a <bold>character walks into the AI's room</bold>.

           This code is on <bold>cAI.PerceiveActorEnter().</bold>

    </li>

    <li>

           When an AI sees <bold>one character attacking another</bold>.

           This code is on <bold>cAI.PerceiveAttack().</bold>

    </li>

</ul>

 

<p>

    When danger might just have appeared, the AI code

    calls <bold>AIPotentialConflict()</bold>. This checks for any

    enemies in the room.

</p>

 

<p>

    If there are enemies in the room, the code adds several

    goals to the AI's list. The goals used are

    in <bold>pAIGoalsConflict</bold>. If the goal is already in

    the AI's list then it is <bold>not</bold> added a second

    time.

</p>

 

 

<br/><section><p><bold><big>Default combat goals</big></bold></p></section>

 

<p>

    The default goals that are added by <bold>pAIGoalsConflict</bold> are:

</p>

 

<ol>

    <li>

           <bold>oAIGoalCombatRetreat</bold> - This causes the AI to test

           out it's odds of winning every once in awhile. If it thinks its

           odds are still good the AI suspends the retreat goal for another

           10 seconds. If there are not enemies left, the retreat goal

           finishes.

    </li>

    <p>

           If the goal decides that the character is in danger,

           based on a call to <bold>AIEnemiesCalculateTheOdds()</bold>,

           then the AI will run away. <bold>pAICombatBravery</bold> is

           used to determine how much the odds have to be stacked against

           the AI before it runs, as well

           as <bold>pAICombatWoundsRetreat</bold> to see if

           it will selfishly abandon it's friends when its wounded.

           If <bold>pAICombatRunDrop</bold> is set to TRUE, the AI will

           drop an item to distract its attackers.

           If <bold>pAICombatRunCallForHelp</bold> is set to TRUE the

           AI will yell for help as it runs.

           If <bold>pAICombatRunForGuard</bold> is set to TRUE, the AI

           will run to the nearest guard and ask it for help. (This

           one is particularly useful for villagers, which should run at

           the slightest sign of danger and seek a guard.)

    </p>

    <li>

           <bold>oAIGoalCombatCallForHelp</bold> - This causes the NPC

           to call out for help once in awhile. AI's listen

           for calls for help in their <bold>PerceiveSpeak()</bold> methods.

           If they detect a call, they determine if the calling NPC

           is a friend, enemy, or neutral. If it's a friend, they

           may run to the friend's aid

           if <bold>pAICombatCallForHelpRespondsFriends</bold> is not 0.

           Likewise, if they hear an enemy calling for help, they may

           decide to join in the kill.

           The <bold>pAICombatCallForHelpRespondsEnemies</bold> property

           affects this chance.

    </li>

    <p>

           If you don't wish a NPC to call for help, override

           its <bold>pAIGoalsConflict</bold> so it doesn't include the goal.

    </p>

   

    <li>

           <bold>oAIGoalAttackEnemies</bold> - This goal causes the NPC

           to attack any enemies in the room. It picks an enemy, by

           various means, and attacks with a weapon. The code also

           has the AI ready a weapon or potentially pick one up.

    </li>

   

    <li>

           <bold>oAIGoalMoveChaseEnemies</bold> - If all the enemies

           in a room are killed, oAIGoalAttackEnemies will finish.

           The next goal in line causes the NPC to chase after enemies

           in neighboring rooms, just in case some of the enemies

           ran away. It finishes when no more enemies

           are left in any neighboring rooms.

    </li>

 

    <li>

           <bold>oAIGoalLootDeadBodies</bold> - Once all the enemies

           are gone, this goal will have the NPC loot all the dead bodies.

           It picks up the most valuable items first. It will hold onto

           them for 30-60 minutes, after which point they're automatically

           deleted. That means that characters who are killed by NPCs have

           30-60 minutes to find the NPC and kill it, or they won't

           get their gear back.

    </li>

   

    <li>

           <bold>oAIGoalMoveToPreCombat</bold> - Finally, after all the

           bodies have been looted, oAIGoalMoveToPreCombat causes the

           NPC to return to the room it was in before combat began.

           Once there, this goal finishes and the AI continues its

           normal routine.

    </li>

</ol>

 

 

<br/><section><p><bold><big>Default goals</big></bold></p></section>

 

<p>

    When a NPC is first created, it automatically adds the following

    goal, as specified in <bold>pAIGoalsFirstActions</bold>.

</p>

 

<ul>

    <li>

           <bold>oAIGoalObjectEquipAll</bold> - Causes the NPC to equip

           any weapons and armor that it has. This ensures it will be

           ready for combat when the time comes.

    </li>

</ul>

 

 

<br/><section><p><bold><big>List of other combat-related goals</big></bold></p></section>

 

<p>

    Of course, you can

    change <bold>pAIGoalsConflict</bold> and <bold>pAIGoalsFirstActions</bold> to

    whatever goals you wish.

</p>

 

<p>

    Some other combat-related goals that I haven't discussed yet

    are:

</p>

 

<ul>

    <li>

           <bold>oAIGoalAttackEnemy</bold> - Has the NPC take a swing

           at an enemy. This is called from oAIGoalAttackEnemies.

    </li>

    <li>

           <bold>oAIGoalCombatAim</bold> - Call this to have the

           NPC change his aim (body part) location to a specific value.

    </li>

    <li>

           <bold>oAIGoalCombatAimChoose</bold> - Call this to have the

           NPC change his aim (body part) location automatically, based on

           various personality properties.

    </li>

    <li>

           <bold>oAIGoalCombatDefend</bold> - Call this to have the

           NPC change his defense location to a specific value.

    </li>

    <li>

           <bold>oAIGoalCombatDefendChoose</bold> - Call this to have the

           NPC change his defense location automatically, based on

           various personality properties.

    </li>

    <li>

           <bold>oAIGoalCombatEnemy</bold> - Call this to have the

           NPC change target a new enemy, based on a passed-in parameter.

    </li>

    <li>

           <bold>oAIGoalCombatEnemyTargetChoose</bold> - Call this to have the

           NPC change his targeted enemy, based on

           various personality properties.

    </li>

    <li>

           <bold>oAIGoalCombatDodgeParry</bold> - Call this to have the

           NPC change his dodge/parry to a specific value.

    </li>

    <li>

           <bold>oAIGoalCombatDodgeParryChoose</bold> - Call this to have the

           NPC change his dodge/parry, based on

           various personality properties.

    </li>

    <li>

           <bold>oAIGoalCombatRetreatCallForHelp</bold> - This goal

           causes the NPC to keep calling out for help until the goal

           is killed. It's added and killed when the NPC retreats

           in oAIGoalCombatRetreat.

    </li>

    <li>

           <bold>oAIGoalObjectEquip</bold> - The NPC will equip

           or unequip a single

           object. It's called by oAIGoalObjectEquipAll.

    </li>

    <li>

           <bold>oAIGoalObjectGetDrop</bold> - The combat AI uses this

           goal to pick up weapons it wishes to equip, to loot bodies,

           or to drop treasure in the hopes of distracting an attacker.

    </li>

    <li>

           <bold>oAIGoalObjectGetWeapon</bold> - The AI will pick up

           a weapon from the ground (or a dead body) if it's better than

           its current weapon, and if it's usable by the NPC. This

           is used by oAIGoalAttackEnemies if the AI's weapon breaks

           or gets dropped.

    </li>

</ul>

 

 

 

<br/><section><p><bold><big>Combat AI properties</big></bold></p></section>

 

<p>

    Many of the combat-related goals rely on properties that affect

    how the AI reacts in combat:

</p>

 

<ul>

    <li>

           <bold>pAICombatAttackEnemySpeed</bold> - How rapidly the AI attacks

           compared to the ideal attack speech for his weapon.

    </li>

    <li>

           <bold>pAICombatAttackEnemySpeedVar</bold> - Amount of variation in

           the AI's attack speed.

    </li>

    <li>

           <bold>pAICombatAimLocation</bold> - Location where the AI tends to aim for.

    </li>

    <li>

           <bold>pAICombatAimSwitch</bold> - Likelihood of the AI aiming for

           a different body part.

    </li>

    <li>

           <bold>pAICombatDefendBodyPart</bold> - Preferred defence location.

    </li>

    <li>

           <bold>pAICombatDefendJustHit</bold> - How likely it is that the AI

           will defend a body part that was just hit.

    </li>

    <li>

           <bold>pAICombatDefendSwitch</bold> - Likelihood of changing defence locations.

    </li>

    <li>

           <bold>pAICombatEnemyTargetAttacker</bold> - Likelihood that the AI

           will attack the last character that attacked it.

    </li>

    <li>

           <bold>pAICombatEnemyTargetSame</bold> - How likely the AI will keep

           attacking the same enemy and not switch.

    </li>

    <li>

           <bold>pAICombatEnemyTargetWeaker</bold> - Causes the AI to

           attack weaker opponents in preference to stronger opponents.

    </li>

    <li>

           <bold>pAICombatEnemyTargetWounded</bold> - Likelihood that the AI

           will attack the most wounded character.

    </li>

</ul>

 

 

<br/><section><p><bold><big>Extra tips</big></bold></p></section>

 

 

<ul>

    <li>

           Because each NPC will have their own combat tactics, you might

           wish to provide a <bold>AIGoalOverride()</bold> method that

           overrides some standard combat goals,

           such as oIAIGoalCombatDefendChoose, with a custom goal. For example:

           You could use this to make the NPC only defend one part of his

           body.

    </li>

</ul>

 

05 AI daily schedules

How to provide an AI with a daily schedule.

 

<section><p><bold><big>AI daily schedules</big></bold></p></section>

 

<p>

    If you want your AI to have a daily schedule, some automatic

    scheduling exists.

</p>

 

<ul>

    <li>

           To have the NPC work in a location,

           set <bold>pAIScheduleLocationWork</bold>. If you want something

           other than a 9-5 job, set <bold>pAIScheduleTimeWork</bold>.

    </li>

    <li>

           To have your NPC return home and sleep,

           set <bold>pAIScheduleLocationLive</bold>. The sleeping

           hours can be set with <bold>pAIScheduleTimeSleep</bold>.

    </li>

    <li>

           To have the NPC recreate (hang out) somewhere,

           set <bold>pAIScheduleLocationRecreate</bold>. The time

           can be set with <bold>pAIScheduleTimeRecreate</bold>.

    </li>

</ul>

 

 

 

<br/><section><p><bold><big>More advanced scheduling</big></bold></p></section>

 

<p>

    The chances are that you'll want your NPC to do more than just

    walk to work, walk home, and sleep. To control more advanced schedule options:

</p>

 

<ol>

    <li>

           Come up with a <bold>lower-case name</bold> for an activity that the

           NPC regularly does, such as "attendmooselodge" to have the

           NPC attend a Moose Lodge meeting.

    </li>

    <li>

           Create an <bold>AIScheduleEnum()</bold> method for the AI that

           fills in ScheduleList with information about "attendmooselodge", such

           as the time and the priority. (You can use this method to make sure

           your NPC only attends the meetings once a week.)

    </li>

    <li>

           Create an <bold>AIScheduleLocation()</bold> method that

           also traps the "attendmooselodge" and returns the room where the moose

           lodge meetings take place.

    </li>

    <li>

           If you want your NPC to be able to answer the question,

           "What are you doing?", then write <bold>AIScheduleWhatDoing()</bold> to

           trap "attendmooselodge".

    </li>

    <li>

           If you want your NPC to do any tasks while at the lodge (based on cAIGoal),

           then you'll need to write a <bold>AIScheduleGoal()</bold> method. You

           can control what the NPC does when its there, what it does before going there,

           and what it does when it's finsihed.

    </li>

    <li>

           If you have several characters who are a member of the Moose Lodge,

           you have another choice: <bold>Create an oFactionMooseLodge</bold> based

           on cFaction, and <bold>impliment the AIScheduleEnum(),

           AIScheduleLocation(), and AIScheduleGoal()</bold> methods in the faction.

           That way, all members of oFactionMooseLodge will show up to the meeting!

    </li>

</ol>

 

<p>

    Note: The scheduling features described here only work

    if the NPC has the oAIGoalSchedule running. This will be run by default.

    However, if you override the default, you may need to include

    oAIGoalSchedule in the goals that you include in the overridden version.

</p>

 

 

 

 

<br/><section><p><bold><big>Foraging - cAIForage</big></bold></p></section>

 

<p>

    A more complex schedule, foraging, is included in

    the <bold>cAIForage</bold> class. This lets you create monsters that wander

    around looking for food, or NPCs which pick up tin cans as they see them.

</p>

 

<p>

    To create a monster (cMonster) or character that forages:

</p>

 

<ol>

    <li>

           <bold>Create your monster or character as usual</bold>, which will be based

           on cMonster or cCharacter, and which are ultimately

           based on cAI.

    </li>

    <li>

           <bold>Add the cAIForageExtra</bold> superclass <bold>above</bold> the cMonster

           or cCharacter class. Placing it above is important sinec cAIForageExtra

           overrides some default behavior.

    </li>

    <li>

           Set <bold>pAIForageExtraWant</bold> to the class of object the AI wants.

           If you don't set it, it will default to "cFood".

    </li>

    <li>

           Fill is <bold>pAIForageExtraEat</bold> to indicate if the foraging creature

           will eat whatever it finds, or keep it in its possession. You can create

           more complicated behaviors by wiring your own AIForafeExtraForageAct().

    </li>

    <li>

           Set <bold>pAIScheduleLocationWork</bold> to a list of rooms where the NPC

           forages, or which are destinations for foraging (and the AI will forage on

           the way).

    </li>

    <li>

           Set <bold>pAIForageExtraLocationWorkItinerant</bold> to cause the AI

           to move sequentially from work room to work room, or to wander between all

           of them.

    </li>

    <li>

           Set <bold>pAIScheduleTimeWork</bold> to the hours that the AI forages.

    </li>

</ol>

 

<p>

    That's it. Your character will wander around picking up (and potentially) eating

    the appropriate objects. Plus, PCs can give the NPC the right sort of objects

    and make the NPC more friendly.

</p>

 

<p>

    You may also wish to modify the following properties and methods:

</p>

 

<ul>

    <li>

           <bold>pAIForageExtraAmountWant</bold> and <bold>pAIForageExtraByValue</bold> cause the AI to "go home" if

           it has foraged enough.

    </li>

    <li>

           <bold>pAIForageExtraDropGoalPriority</bold> and <bold>pAIForageExtraRoomGoalPriority</bold> control

           whether the AI stops to forage in the middle of combat.

    </li>

    <li>

           <bold>pAIForageExtraGiveLike</bold> and <bold>pAIForageExtraGiveStopAttack</bold> affect how much the NPC likes

           a PC that provides the foraged items... such as giving food to a foraging monster.

    </li>

    <li>

           <bold>pAIScheduleIdleWork</bold> is used to describe the character

           foraging. Defaults for monsters eating are already provided in cMonster.pAIScheduleIdleWork.

    </li>

    <li>

           <bold>pAIForageExtraGiveNoThanksNarrate, pAIForageExtraGiveNoThanksSpeak,

           pAIForageExtraGiveThanksNarrate, pAIForageExtraGiveThanksSpeak,

           pAIForageExtraShowNoWantNarrate, pAIForageExtraShowNoWantSpeak,

           pAIForageExtraShowWantNarrate, and pAIForageExtraShowWantSpeak</bold> all

           control the AI's narrated and spoken reaction.

    </li>

    <li>

           <bold>pAIScheduleWhatDoingWork</bold> lets the NPC say what it's looking

           for when a player asks.

    </li>

    <li>

           <bold>AIForageExtraForageAct()</bold> can be used to provide more complex

           foraging activities than just eating and picking up objects.

    </li>

</ul>

 

 

<br/><section><p><bold><big>Guarding an exit - cGuardExitExtra</big></bold></p></section>

 

<p>

    To make a NPC guard an exit (and prevent players from leaving via the exit),

    create a character (based on cCharacter or whatever):

</p>

 

<ol>

    <li>

           Add the <bold>cGuardExitExtra</bold> super-class to the object.

    </li>

    <li>

           Fill in <bold>pAIScheduleLocationWork</bold> with the room that the guard

           is protecting.

    </li>

    <li>

           <bold>pAIScheduleTimeWork</bold> should be filled in with the hours that

           this guard keeps. If the guard isn't there all the time then make sure to

           have other guards show up, or players only have to wait for the

           guard to leave.

    </li>

    <li>

           <bold>pGuardExitExtraBlockDirection</bold> should be filled with the

           direction that the guard prevents players from going.

    </li>

    <li>

           <bold>pGuardExitExtraCanPassProperty</bold> the the property that will

           be set on the PC that indicates that the PC is allowed to pass.

    </li>

</ol>

 

<p>

    You might also wish to set:

</p>

 

<ul>

    <li>

           <bold>pGuardExitExtraBlockNarrate</bold> for a narration of how the guard

           blocks the PC from going in the blocked direction.

    </li>

    <li>

           <bold>pGuardExitExtraBlockSpeak</bold> is spoken by the guard when he

           blocks the PC.

    </li>

    <li>

           <bold>pGuardExitExtraFavor</bold> controls whether the player can get

           friendly with the guard and ask for a favor.

    </li>

    <li>

           <bold>pGuardExitExtraFavorGranted</bold> is what the guard says when it

           grants the favor of passage.

    </li>

    <li>

           <bold>pGuardExitExtraWaitForObject</bold> ensures that the guard waits

           around until another guard shows up.

    </li>

</ul>

 

 

 

<br/><section><p><bold><big>Beggars - cBeggarExtra</big></bold></p></section>

 

<p>

    You can make a NPC that begs for food, drink, money, or whatever using

    the cBeggarExtra class.

</p>

 

<ol>

    <li>

           Add the <bold>cBeggarExtra</bold> super-class to the object, on top

           of cRaceElf, or whatever.

    </li>

</ol>

 

<p>

    That's it! The NPC will beg for food, drink, and money whenver a player enters

    the room, or it enters a room with a player.

</p>

 

<p>

    You might also wish to:

</p>

 

<ol>

    <li>

           Add a <bold>schedule</bold> to the NPC so it wanders around and begs.

    </li>

    <li>

           Change <bold>pAILikeEquip</bold> to control what comments it makes about what

           the player is carrying.

    </li>

    <li>

           <bold>pAIOfferCanBribe</bold> defaults to TRUE, but if you don't want the

           beggar to accept money then set this to FALSE.

    </li>

    <li>

           Change <bold>pAIOfferForget</bold> to adjust how frequently gifts can

           be given to the beggar.

    </li>

    <li>

           Change <bold>pAIOfferMaxAmount</bold> to determine how much the beggar will accept.

    </li>

    <li>

           <bold>pAIOfferMinLike</bold> controls how much the NPC must like the player before

           accepting gifts.

    </li>

    <li>

           <bold>pAIValueObject</bold> defines what objects the beggar likes. The default is

           for food and drink.

    </li>

    <li>

           <bold>pBeggarExtraMinTime</bold> affects the number of seconds that must ellapse

           between each "beg", so that beggars don't beg too often.

    </li>

    <li>

           <bold>pBeggarExtraSpeak</bold> is a list of phrases that the beggar will speak.

           You'll need to change this if you modify <bold>pAIValueObject</bold>.

    </li>

    <li>

           <bold>pBeggarExtraSpeakScriptPriority</bold> controls the priority of the begging script,

           ensuring that the beggar doesn't beg when running away from a monsters.

    </li>

</ol>

06 AI memory of characters – cAIMemory

Details how an AI remembers information about player characters.

 

<section><p><bold><big>AI memory of characters - cAIMemory</big></bold></p></section>

 

<p>

    In most MMORPGs/MUDs/IF software, AIs (if they exist) remember

    very little about a player character. <bold>Circumreality AIs remember

    a lot of information about the player characters they meet.</bold>

</p>

 

<p>

    At the heart of an AI's memory is the <bold>cAIMemory</bold> class,

    which is a superlcass of <bold>cAI</bold> and <bold>cFaction</bold>.

</p>

 

<br/><section><p><bold><big>Basic cAIMemory methods</big></bold></p></section>

 

<p>

    The cAIMemory class maintains a database of all the player

    characters it has met, stored in <bold>pAIMemory</bold>. For

    each character, it stores several hundred pieces of "information",

    such as whether or not the AI likes the character, the last

    time they met, what the character's favorite color is, etc.

</p>

 

<p>

    Each piece of information is labled with a lower case

    string, such as "favoritecolor", and is stored as a sub-list.

    If your code changes elements within the sub-list, the AI

    will remember the information.

</p>

 

<p>

    Every AI (derived from cAI) remembers a list of dangerous enemies,

    as well as friends that will help in combat. (This list is different

    than the like/dislike list also supplied for AI's.)

</p>

 

<p>

    To access an AI's memory about a character's favorite color,

    call <bold>vFavoriteColor = oAI.AIMemoryGet (Actor,

    "favoritecolor", FALSE);</bold> Actor is the player character's

    object. "favoritecolor" is the piece of information to access.

    FALSE indicates that if the memory doesn't exist then don't

    bother to create it. When the method returns, vFavoriteColor

    will be a list, with (hypothetically) the first element of the

    list set to the AI's memory of the character's favorite

    color. If the AI had never stored such a memory NULL will

    be returned from AIMemoryGet().

</p>

 

<p>

    An AI's memory is based on the following (fundamental) methods:

</p>

 

<ul>

    <li>

           <bold>AIMemoryActorExist()</bold> - Tests to see if the AI

           remembers anything at all about the actor.

    </li>

    <li>

           <bold>AIMemoryActorRemove()</bold> - Causes the AI to forget

           everything it knows about the actor.

    </li>

    <li>

           <bold>AIMemoryGet()</bold> - Gets (or adds) a memory to the

           AI, as discussed above.

    </li>

    <li>

           <bold>AIMemoryRemove()</bold> - Causes the AI to forget

           a specific piece of information it knows about the actor,

           such as the actor's favorite color.

    </li>

</ul>

 

<p>

    An AI manages its database, occasionally deleting old data.

    You should be aware that:

</p>

 

<ul>

    <li>

           AIs will remember every player character they meet. They <bold>won't

           ever forget a player character,</bold> unless the player character

           is deleted from the database, gDatabaseActors. This ensures

           that players won't ever feel cheated by NPCs forgetting them.

    </li>

    <li>

           AIs will also remember other NPCs. They may, however, <bold>forget

           about the NPCs if they are deleted</bold>, even if the NPCs

           are backed up to a database.

    </li>

    <li>

           AIs will only store about <bold>200 pieces of information</bold> on a character before

           deleting old ones. The limit is set by gAIMemoryTrim.

    </li>

    <li>

           If a character hasn't been ecountered for <bold>more than

           two weeks</bold> by the AI, the memory limit is set to gAIMemoryTrim / 2.

    </li>

    <li>

           <bold>Important:</bold> If you want a character (that uses cAIMemory)

           in a multiplayer world to <bold>remember across reboots</bold> then you

           need to also base the character off cSaveToDatabase (see the turtorial).

    </li>

</ul>

 

 

 

 

<br/><section><p><bold><big>Advanced memory</big></bold></p></section>

 

<p>

    You might also wish to do the following:

</p>

 

<ul>

    <li>

           Sometimes you will want to have one NPC in two or more different rooms at

           once. This often happens when you create one copy of a village that exists

           before an orc attack, and another version after the orc attack. You may

           want a few NPCs to survive the orc attack. Thus, they need to be in two

           places at once. To allow for this, create two copies of the NPC. However,

           you'll need to have their memories of player characters stay in sync.

           To do this, have one of NPCs alias its memory to the other NPC

           by using <bold>pAIMemoryAliasTo</bold>.

    </li>

</ul>

07 Factions – cFaction

A class that can be used to create factions and guilds for PCs and NPCs.

 

<section><p><bold><big>06. Factions - cFaction</big></bold></p></section>

 

<p>

    A <bold>faction</bold> is a group of characters, either NPCs or PCs.

    Factions are also known as guilds, but are not limited to

    official organizations. Members of a village or town could all

    belong to a faction "MemberOfTownXXX". Likewise, members of

    a race or culture could all belong to a faction.

</p>

 

<p>

    Making NPCs a member of one or more factions is useful for

    the following reasons:

</p>

 

<ul>

    <li>

           NPCs will be able to <bold>derive knowledge</bold> that's "known" to the

           faction. For example, the villagers of Hermansville (a faction)

           all know where the temple is in Hermansville, and all know

           about the goblin raiders to the north.

    </li>

    <li>

           When a <bold>player character befriends a NPC in a faction</bold>, he

           ever-so-slightly increases the reaction of all members of

           the faction. Thus, is a player makes friends with half the NPCs

           in town, the other NPCs will be friendly also. This works

           in reverse.

    </li>

    <li>

           <bold>NPCs can have "enemy" factions.</bold> If a player character befriends

           an enemy faction (such as "Enemies of Hermansville"),

           he will not get as good of a reaction (from "Villagers of

           Hermansville").

    </li>

</ul>

 

 

<p>

    Players may wish to be part of a faction for the following reasons:

</p>

 

<ul>

    <li>

           <bold>Standard MUD/MMORPG guid features</bold>, such as guild E-mail.

    </li>

    <li>

           If a PC is a member of a faction, and he makes a good impression

           on an NPC, <bold>the NPC's reaction will improve slightly for

           all members of the faction.</bold> Thus, if you're a member

           of a guild, your actions affect how all the other guild members

           are seen, positively or negatively.

    </li>

</ul>

 

 

<br/><section><p><bold><big>Creating a faction</big></bold></p></section>

 

<p>

    To create a faction:

</p>

 

<ol>

    <li>

           <bold>Create an object based on cFaction.</bold> Since cFaction

           is based on cAIMemory, the faction's information will automatically

           be saved across reboots.

    </li>

    <li>

           Make sure the object <bold>is an object</bold>, and not a class.

    </li>

    <li>

           Fill in the faction's <bold>pNLPNounName</bold> and <bold>pNLPParseName</bold>,

           as usual.

    </li>

    <li>

           <bold>pFactionSize</bold> should be filled with the guestimated

           number of members of the faction. The faction size controls how

           much a NPC's reaction to the faction is affected by its

           individual members. Thus, if a player is a member of a faction,

           and makes a good impression on a NPC, the NPC's reaction

           to the faction will only increase by 1/pFactionSize.

    </li>

</ol>

 

 

<br/><section><p><bold><big>Advanced factions</big></bold></p></section>

 

<p>

    You may also wish to fill in the following faction information:

</p>

 

<ul>

    <li>

           <bold>pAIConvScripts</bold> dictates what conversations scripts are

           common to all members of a faction.

    </li>

    <li>

           <bold>pAIConvStories</bold> lists all stories known by faction members.

    </li>

    <li>

           <bold>pAIFactionAntiFactions</bold> provides an easier way for one

           faction to dislike another.

    </li>

    <li>

           <bold>pAIFactoids</bold> lists factoids known by all faction members.

    </li>

    <li>

           <bold>pAIKnowledgeConversationStateExtreme</bold> lists conversation

           states that have a large impact on faction members. For example: Talking

           about "fishing" to members of the fisherman's guild.

    </li>

    <li>

           <bold>pAIKnowledgeKnow</bold> is a list of knowledge that faction

           members will know.

    </li>

    <li>

           <bold>pAILikeDefault</bold> is used by AILikeFirstImpressions().

    </li>

    <li>

           <bold>pAILikeEquip</bold> is used by AILikeFirstImpressions().

    </li>

    <li>

           <bold>pAILikeForget</bold> controls how quickly factions forget about

           player characters.

    </li>

    <li>

           <bold>pAILikeGender</bold> and <bold>pGender</bold> are used by AILikeFirstImpressions().

    </li>

    <li>

           <bold>pAILikeRace</bold> is used by AILikeFirstImpressions().

    </li>

    <li>

           <bold>pAILikeReputation</bold> controls how much a player's reputation

           affects faction members.

    </li>

    <li>

           <bold>pAILikeRoom</bold> causes the NPC to like the player more

           if the NPC is in a specific room.

    </li>

    <li>

           <bold>pAILikeSkills</bold> is used by AILikeFirstImpressions().

    </li>

    <li>

           <bold>pAnonymous</bold> causes the faction to be known to only

           a few NPCs.

    </li>

    <li>

           If you se <bold>pIsInvisible</bold> to TRUE then players won't be

           able to see the faction when they type, "What is my reputation?"

    </li>

    <li>

           Set <bold>pAIFactionIsHobby</bold> to TRUE to use the faction object

           as a NPC hobby, such as fishing, bowling, or Star Trek fan.

    </li>

    <li>

           <bold>AILikeEquip()</bold> determines what kind of equipment faction

           members like, such as black robes, etc.

    </li>

    <li>

           <bold>AILikeFirstImpressions()</bold> controls how much faction members

           like other characters.

    </li>

</ul>

 

 

08 Reputations

Remember a player character's reputation.

 

<section><p><bold><big>Reputations</big></bold></p></section>

 

<p>

    As discusses in the factions tutorial, a player character's reputation

    precedes him because of the memory stored in the cFaction object.

</p>

 

<p>

    You might also wish a faction-independent method for storing

    a reputation, such as how heroic the character is, or perhaps

    his wealth.

</p>

 

<p>

    The faction-independent reptutation system works as follows:

</p>

 

<ol>

    <li>

           Come up with a <bold>lower-case language-independent</bold> string

           to represent the character's reputation, such as "goodfisherman" to

           indicate how high the player's reputation is based on his fishing

           skills. (Make sure that you don't have any spaces.)

    </li>

    <li>

           You will need to make a global, <bold>gReputationNameGoodFisherman</bold> with

           a language-dependent name of the reputation, like "Good fisherman".

    </li>

    <p>

           This global will be accessed by calls

           to <bold>ReputationToName()</bold>, which is used when the

           player types, "What is my reputation?"

    </p>

    <li>

           As the player character completes quests (or whatever), you can

           increase (or decrease) the reputation by

           calling <bold>ReputationSet()</bold>, passing in "goodfisherman" and

           a value from -10 (lower the reputation) to 10 (increase the reputation).

    </li>

    <li>

           To make NPCs that are impressed by a reputation, provide

           a <bold>pAILikeReputation</bold> property for the NPC.

    </li>

    <li>

           Likewise, to make all NPCs in a faction impressed by a reputation,

           set the <bold>cFaction.pAILikeReputation</bold>.

    </li>

    <li>

           You may also wish to write your

           own <bold>AILikeReputation()</bold> method for NPCs or their factions.

    </li>

</ol>

 

<p>

    That's all! As a player's "goodfisherman" reputation improves, NPCs

    will like (or trust) the player character more.

</p>

09 AI like and trust of characters – cAI

Talks about APIs use to get and set how much an AI likes/dislikes and trusts/mistrusts a character.

 

<section><p><bold><big>AI like and trust of characters - cAI</big></bold></p></section>

 

<p>

    As players wander through an interactive fiction title, they

    will encounter NPCs. In most text IF and MUDs, NPCs instantly

    forget about PCs as soon as the PCs leave the room. Not so

    with Circumreality. NPCs remember a variety of information about the PCs

    in the cAIMemory class.

</p>

 

<p>

    Two of the most commonly remembered pieces of information are

    whether or not the NPC liked the PC, and whether or not the NPC

    trusts the PC. "Like" can be used to determine how much information

    the NPC will reveal to the PC. "Trust" indicates what type

    of tasks the NPC will give to the PC.

</p>

 

<p>

    Both the like and trust memories are stored as values, ranging

    from -10 to 10. Here is a sample of what the values mean:

</p>

 

<table width=100%>

    <tr>

           <td/>

           <td><bold>Like</bold></td>

           <td><bold>Trust</bold></td>

    </tr>

    <tr>

           <td>+10</td>

           <td>

                  The NPC likes the PC a lot, and will do anything for the PC.

           </td>

           <td>

                  The NPC trusts the PC with his/her life.

           </td>

    </tr>

    <tr>

           <td>+6</td>

           <td>

                  The NPC would consider the PC a friend.

           </td>

           <td>

                  The NPC trusts the PC with important tasks.

           </td>

    </tr>

    <tr>

           <td>+3</td>

           <td>

                  The NPC thinks of the PC as a friendly acquaintance.

           </td>

           <td>

                  The NPC will give the PC minor tasks whose failure

                  won't signficantly hurt the NPC.

           </td>

    </tr>

    <tr>

           <td>0</td>

           <td>

                  Neutral

           </td>

           <td>

                  Neutral

           </td>

    </tr>

    <tr>

           <td>-3</td>

           <td>

                  The NPC dislikes the PC.

           </td>

           <td>

                  The NPC might give the PC a task, just to see what happens,

                  but won't expect it to be completed.

           </td>

    </tr>

    <tr>

           <td>-6</td>

           <td>

                  The NPC considers the PC an enemy.

           </td>

           <td>

                  The NPC doesn't trust the PC as far as he can throw him.

           </td>

    </tr>

    <tr>

           <td>-10</td>

           <td>

                  Hatred.

           </td>

           <td>

                  The NPC thinks the PC is completely incompetent.

           </td>

    </tr>

</table>

 

<p/>

 

<p>

    To find out how much a NPC likes or trusts a PC (or other NPC),

    call oNPC.<bold>AILikeGet()</bold>. This will return a list

    with the first element being the like value, and the second

    with the trust value.

</p>

 

<p>

    You can adjust how much a NPC likes or trusts a PC (or other

    NPC) by calling oNPC.<bold>AILikeSet()</bold> whenever

    the PC performs an action that gives a positive or negative

    impression of the PC to the NPC. The values passed into AILikeSet()

    for like/trust depend upon how significant the player's actions

    are:

</p>

 

<table width=100%>

    <tr>

           <td/>

           <td><bold>AILikeSet() Like</bold></td>

           <td><bold>AILikeSet() Trust</bold></td>

    </tr>

    <tr>

           <td>+10</td>

           <td>

                  PC does something for the NPC that the NPC has always desired,

                  but not even the NPC's best friends have done it.

                  For example: Giving the NPC $1,000,000.

           </td>

           <td>

                  The PC saves the NPC's life, or completes a very important

                  task.

           </td>

    </tr>

    <tr>

           <td>+6</td>

           <td>

                  The PC does something very nice for the NPC.

           </td>

           <td>

                  The PC completes a major task for the NPC.

           </td>

    </tr>

    <tr>

           <td>+3</td>

           <td>

                  The PC does something nice for the NPC.

           </td>

           <td>

                  The PC completes a minor task.

           </td>

    </tr>

    <tr>

           <td>+1</td>

           <td>

                  The PC smiles at the PC, or some other polite formality.

           </td>

           <td>

                  The PC pays a bill, or some other small task.

           </td>

    </tr>

    <tr>

           <td>-1</td>

           <td>

                  The PC doesn't smile at the PC, or some other small social mistake.

           </td>

           <td>

                  The PC is late for an appointment.

           </td>

    </tr>

    <tr>

           <td>-3</td>

           <td>

                  The PC comes off as being unintentionally rude.

           </td>

           <td>

                  The PC misses an appointment.

           </td>

    </tr>

    <tr>

           <td>-6</td>

           <td>

                  The PC is intentionally rude or mean.

           </td>

           <td>

                  The PC fails to complete an important task.

           </td>

    </tr>

    <tr>

           <td>-10</td>

           <td>

                  The PC commits an awful offense.

           </td>

           <td>

                  The PC attacks the NPC.

           </td>

    </tr>

</table>

 

<p/>

 

 

<br/><section><p><bold><big>AILikeSet() intricacies</big></bold></p></section>

 

<p>

    AILikeSet() is not a simple function that just instantly changes

    the NPC's view of the PC. It handles quite a few smaller details:

</p>

 

<ul>

    <li>

           Calling AILikeSet() will increase or decrease the PC's

           like/trust based on the value passed in.

           It will <bold>not</bold> change

           the NPC's like/trust immediately to the new values, but

           will <bold>gradually increase/decrease the NPC's like/trust each time

           AILikeSet() is called.</bold> Therefore, for a PC to make a NPC a friend,

           the PC will need to be repeatedly nice to a NPC.

    </li>

    <li>

           AILikeSet() <bold>will not adjust a NPC's like/trust higher than

           the like/trust parameter</bold> (if the parameter is a positive value),

           or lower than the like/trust parameter (if the parameter is

           negative). This means that smiling (a +1.0 like parameter) will

           only get a player so far, and smilling a thousand times will

           not make the player best friends with the NPC. To become best

           friends the PC will have to do several best-friends activities

           first.

    </li>

    <li>

           Some NPCs will have their like/trust adjusted more quickly

           than others, depending upon

           their <bold>pAILikeChangable</bold> attribute. If the values

           are high, the NPC will quickly take a liking (or dislking) to

           a PC. If they're low, the NPC is slow to change its mind.

    </li>

    <li>

           If the NPC belongs to any factions (which is probable),

           then <bold>AILikeSet() will subtly affect all NPCs in

           the same faction.</bold> Word gets around, and being likable

           or trustworthy

           to a few villagers in town will gradually improve a PC's like/trust

           to all viallager.

    </li>

    <li>

           If the PC belongs to any factions (which is probable),

           then <bold>AILikeSet() will subtly affect the NPC's reaction

           to other members of the faction.</bold> If a PC is a member

           of the "XYZ Guild" and makes a good impression on the NPC,

           the other members of the XYZ Guild will get a better

           response. This works the opposite way too; making a bad

           impression reflects on the entire guild.

    </li>

    <li>

           Likewise, <bold>any factions that the NPC belongs to will form

           an opinion about any factions that the PC belongs to.</bold> If

           a NPC from XYZ Guild upsets a NPC in Hermansville, then

           all NPCs in Hermansville will have (mildly) negative reactions

           to all members of the XYZ Guild.

    </li>

    <li>

           NPCs (and factions) gradually forget about offenses and favors

           that a PC (or faction) does. The speed of this forgetfullness

           (or forgiveness) is controlled by <bold>pAILikeForget</bold>.

    </li>

</ul>

 

 

 

<br/><section><p><bold><big>AILikeGet() intricacies</big></bold></p></section>

 

<p>

    AILikeGet() also has some intricate behavior that you should

    be aware of, especially when the NPC first meets the PC:

</p>

 

<ul>

    <li>

           Some NPCs are more likely to "follow the crowd" while others

           are more independent thinkers. This personality trait manifests

           itself when a NPC has met several PCs from a faction (XYZ Guild),

           and then starts liking/trusting (or disliking/mistrusting) all

           members of the guild based on the few meetings. If the

           NPC's <bold>pAILikeIndividual</bold> is high, the NPC will

           assume each PC is different and won't stereotype the PCs

           in XYZ Guild based on other members of XYZ Guild. If pAILikeIndividual

           is low, the NPC assumes that all members of XYZ Guild are

           more or less the same as far as likability and trustworthiness goes.

    </li>

    <li>

           If the NPC has never met the PC, the like/trust values

           default to <bold>pAILikeDefault</bold>. Some NPCs are naturally

           friendly while others may naturally be untrustworthy.

    </li>

    <li>

           The NPC can also form an opinion based upon what the

           PC is carrying,

           using <bold>pAILikeEquip</bold> and <bold>pAILikeEquipValue</bold>. Some AI's

           might like/trust wealthy characters, ones with weapons, or

           ones wearing red shoes.

    </li>

    <li>

           If the NPC is in the same faction as the PC, such as

           them bothing coming from the same village,

           then <bold>pAILikeFaction</bold> affects how much the

           NPC's like/trust is affected by common factions.

    </li>

    <li>

           <bold>pAILikeGender</bold> lets a NPC be more/less

           friendly/trusting with members of the same/opposite gender.

    </li>

    <li>

           <bold>pAILikeRace</bold> will cause a NPC to like/dislike

           and trust/mistrust PCs from other races. Elves might dislike

           orcs, and mistrust dwarves.

    </li>

    <li>

           <bold>pAILikeRoom</bold> causes the NPC to like/trust the

           PC more if the NPC is in a specific room, and feels more

           comfortable there.

    </li>

    <li>

           <bold>pAILikeSkills</bold> can be used to make NPCs like/trust

           charismatic PCs, strong PCs, or PCs with degrees in

           quantum mechanics.

    </li>

    <li>

           If the NPC's factions list, <bold>pFactions</bold>, includes

           "anti-factions", then the more the PC is liked by the anti-faction,

           the more the PC will be disliked by the NPC.

    </li>

</ul>

 

 

<br/><section><p><bold><big>Emotes based on like/trust</big></bold></p></section>

 

<p>

    When a PC says or does something that causes a NPC's perception

    of the PC to change, AILikeSet() is called. One of the parameters

    allows the NPC to emote based on how much the like/trust changed.

    For example: If the PC says something nice and improves their "like"

    score with the NPC, the NPC could emote a brief smile, while a

    small social mistake might cause a decrease in "like" and make

    the NPC purse its lips slightly.

</p>

 

<p>

    To do this, AILikeSet() calls <bold>AILikeEmoteChanged()</bold> with

    modified versions of the like/trust parameter. The default method

    includes a variety of short smiles, lip pursing, and flashes

    of anger. You may wish to replace this functionality for some NPCs...

    One NPC could clear his throat to indicate displeasure. Another

    could mutter something under his breath.

</p>

 

<p>

    A NPC's like/trust is also shown when the NPC sees a PC

    enter the room. This calls <bold>AILikeEmoteFirstMeet()</bold>.

    If a PC is universally disliked within the town he will get

    glares as he walked through the streets.

</p>

 

<p>

    Similarly, if the NPC walks into a room with other PCs, the NPC will

    find the most liked/trusted (or least liked/trusted) PC in

    the room and call <bold>AILikeEmoteFirstMeet()</bold> to

    emote.

</p>

 

<p>

    AILikeEmoteFirstMeet() is also called when a conversation between

    a NPC and a PC is started. The AI's reactions will be more

    severe than if the AI is just passing the PC on the street.

</p>

 

<p>

    As with AILikeEmoteChanged(), you may wish

    to override <bold>AILikeEmoteFirstMeet()</bold> as appropriate

    for the NPC.

</p>

 

<br/><section><p><bold><big>Personality traits</big></bold></p></section>

 

<p>

    You may have noticed, but I just mentioned about ten properties

    that are "personality traits", such as how quickly the AI forgets

    and forgives, whether it likes men more than woman, etc.

    More are yet to come.

</p>

 

<p>

    You can set the properties for NPCs to fine-tune their

    personality. However, if you <bold>do not specify</bold> the

    personality properties, random and reasonable values

    will be generated when the NPC is created.

</p>

10 Emotions – cAI

AIs can have emotions for happy/sad and anger/fear.

 

<section><p><bold><big>Emotions - cAI</big></bold></p></section>

 

<p>

    AI's store two emotions: <bold>Happy/sad</bold> and <bold>Angry/afraid</bold>. Each

    emotion ranges from -10 (very sad or very afraid) to +10 (very happy

    or very angry).

</p>

 

<p>

    You can get the AI's emotions using <bold>AIEmotionsGet()</bold>.

</p>

 

<p>

    The emotions can be adjusted or set

    with <bold>AIEmotionsSet()</bold>. This either sets the emotions

    directly, or it adjusts the emotions, making the NPC happier, sadder,

    angrier, or more afraid. The amount that a NPC's emotions

    are changed is affected by <bold>pAIEmotionsChangable</bold>,

    allowing for some NPCs to be more excitable than others.

</p>

 

<p>

    Emotions gradually return to 0 over time. (Actually, they

    eventually return to the AI's <bold>pAIEmotionsDefault</bold> over

    time.) The speed of this return is controlled

    by <bold>pAIEmotionsCalmDown</bold>.

</p>

 

<p>

    You may have the AI's emotions changed based on conversations

    the PCs are having with the AI, or good/bad news that the AI

    hears, etc.

</p>

 

<p>

    An AI's emotions are automatically adjusted during combat.

    When the AI determines what its odds are for winning, this

    is expressed on the AI's face as either anger (confident of

    winning) or fear (almost ready to run).

    If an AI sees an enemy killed or knocked unconscious it becomes

    happier, and vice versa for friends.

</p>

11 NPC conversations – cAI

This tutorial provides the details about how conversations with NPCs work.

 

<section><p><bold><big>NPC conversations - cAI</big></bold></p></section>

 

 

<p>

    In Circumreality, players converse with NPCs using the chat window, the

    same way that players talk with other players. The NPCs use

    simple natural language processing (NLP) to understand what

    the player is asking for.

</p>

 

 

 

 

<br/><section><p><bold><big>Programatically starting up a conversation</big></bold></p></section>

 

<p>

    When an AI hears speech (PerceiveSpeak()), or sees an emote

    (PerceiveEmote()), or the PC identifies himself (PerceiveIdentifySelf()),

    it's first task is to determine if the PC is talking to the AI

    or another PC or NPC in the room. To do this, the AI

    calls <bold>AIConversationIsTalkingToMe()</bold>. This method

    uses various tricks to determine if the PC is talking with the AI.

    If it is, a conversation is started up (see below) and the

    method returns TRUE. If not, the method returns FALSE.

    You will probably <bold>not</bold> need to call AIConversationIsTalkingToMe()

    though.

</p>

 

<p>

    If AIConversationIsTalkingToMe() starts up a conversation,

    it calls <bold>AIConversationStart()</bold>. This method adds

    an entry to <bold>pAIConversations</bold> so the AI knows

    what characters it's talking to. It also causes

    AILikeEmoteFirstMeet() to be called so that players can tell

    how much the AI likes them. Finally, it adds

    the <bold>oAIGoalConversation</bold> goal to the AI at

    priority level 10. This causes the AI to stop doing what it

    was doing until the conversation is finished (unless there

    is a higher priority goal). The conversation will be finished

    when the PC says "Goodbye" (or the equivalent), or leaves the

    room.

</p>

 

<p>

    You may wish to call AIConversationStart() if you wish your AI

    to start up a conversation with a PC on its own. (See below.)

</p>

 

<p>

    When the AI walks into a room, or when the AI sees the player

    character walk into its room, <bold>AIConversationWantToStart()</bold> is

    called. This lets the AI start up a conversation with PCs.

    For example: An inn keeper might ask the player if they'd

    like a room tonight without the player's ever approaching

    the inn keeper. The default method will

    have AI's who see well-liked PCs stop and say hello.

</p>

 

<p>

    You may wish to write your own AIConversationWantToStart() for

    chatty NPCs like inn keepers, friendly NPCs who know the PC,

    or beggars. You will need to call AIConversationStart() to

    start up the conversation though.

</p>

 

 

 

<br/><section><p><bold><big>Conversing</big></bold></p></section>

 

<p>

    When a PC speaks, emotes, or introduces itself to an AI, the

    speech (or emote or introduction) is passed

    onto <bold>PerceiveConversation()</bold>. The method isn't

    called immediately, but has a 1 second delay. This makes for

    more realistic response times, as well as ensuring two AI's

    in conversation don't get into an infinite loop.

</p>

 

<p>

    The PerceiveConversation() method does this following:

</p>

 

<ol>

    <li>

           Sets up all the <bold>rule-sets</bold> to be used by the NLPParse() function.

           For more information on parsing and the NLPParseXXX() functions,

           see the tutorials on command-line parsing.

    </li>

    <li>

           Calls <bold>NLPParse().</bold>

    </li>

    <li>

           Based on the parse results, it calls

           into <bold>ParseC_XXX()</bold> to determine if the parse makes sense.

           Command-line parsing calls into Parse_XXX() methods.

    </li>

    <li>

           The ParseC_XXX() method with the highest score is chosen,

           and its <bold>ParseActC_XXX()</bold> method is called to have

           the AI react to what the player said.

    </li>

    <li>

           The AI may respond to the player's question, and in turn

           ask one of it's own by calling <bold>AIConversationAskQuestion()</bold>. For

           example, the AI may ask the PC his name.

    </li>

</ol>

 

 

<p>

    Now for some more details on this process:

</p>

 

<br/><section><p><bold><big>Rule sets</big></bold></p></section>

 

<p>

    For the natural language processor to work, it must have

    a number of <bold>"rule sets" loaded into a "parser".</bold> In the case

    of conversations, the parser name used is gConversationParser.

    The rule sets can be customized for each NPC, but the

    default behaviour is to include the following rule sets:

</p>

 

<ul>

    <li>

           <bold>rParserCommon</bold> - These are rules that are common

           to both the command-line parser and the conversations. They

           do parse converstions like "colour" to "color", and

           "we're" to "we are".

    </li>

    <li>

           <bold>rParserConversation</bold> - Parsing rules that all

           AI's understand. These include "What time is it?" and "Do

           you know where NAME lives?".

    </li>

    <li>

           <bold>Pronouns</bold> - Rules that convert pronouns, such

           as "it" and "him", to object IDs. This allows conversations

           to use the pronouns. For more information on

           pronouns in a conversation

           see <bold>AIConversationPronounSet()</bold> and <bold>AIConversationPronounSetString()</bold>.

    </li>

    <li>

           <bold>NPC and speaker possessions</bold> - The names of all

           objects held by the NPC and the speaker (but not those in

           containers). This allows conversations to reference objects

           held by the NPC or the player.

    </li>

    <li>

           <bold>Names that the NPC knows</bold> - A list of all the

           PC names that the AI has learned when the characters

           identify themselves. The rules convert the names into

           the character's object ID. These are the names from CustomNameGet()

           and CustomNameSet().

    </li>

    <li>

           <bold>Names of all NPCs on the map</bold> - A list of all

           NPCs in the NPC's original map, so that their names are

           converted to object IDs. It's useful for villages and cities (which

           are all one one map), so that NPCs will know about other

           NPCs in the city. See <bold>pAnonymous</bold> for information

           on making a NPC unknown to other NPCs.

    </li>

    <li>

           <bold>Names of all rooms on the map</bold> - A list of all

           rooms in the NPC's original map, so that their names are

           converted to object IDs. It's useful for villages and cities (which

           are all one one map), so that NPCs will know where the "inn" is,

           and where the "church" is. See <bold>pAnonymous</bold> for information

           on making a NPC unknown to other NPCs.

    </li>

    <li>

           <bold>Names of all zones, regions, and mapps</bold> - Lets the NPC answer

           questions about specific zones, regions, and maps.

    </li>

    <li>

           <bold>Names of all factions</bold> - Lets the NPC answer

           questions about specific factions.

    </li>

    <li>

           <bold>Names of all skills</bold> - Lets the NPC answer

           questions about specific skills.

    </li>

    <li>

           <bold>pAIConversationParser</bold> - This property may

           provide additional parsers for the NPC's AI. The extra

           parsers can, in turn, add new rule sets. (See below.)

    </li>

    <li>

           <bold>Factions</bold> - If the NPC is a member of any factions,

           the NPC's factions' AIConversationParser() method will be

           called. This can add rule sets of its own, which are specific

           to a faction. For example: Members of the "Leatherworkers guild" might

           include rule sets that allow players to ask about leatherworking.

    </li>

    <li>

           <bold>AIConversationParser()</bold> - NPCs can provide their

           own AIConversationParser() methods, which can add new rule

           sets. (See below.) For example: AI's from the cMerchant class

           might include rule sets for "How much does XXX cost?" and "I

           want to buy XXX."

    </li>

</ul>

 

<p>

    cAI.PerceiveConversation() adds these parser's by calling

    into the AI's <bold>AIConversationParser()</bold>. This method

    adds many of the above rules into the gConversationParser parser.

    It also calls <bold>oParserConversation.AIConversationParser()</bold>,

    which adds the rest. This object supplies most of the conversation

    code and methods that are common to all AIs, so that each AI

    doesn't have to keep track of the hundreds of conversation methods,

    such as ParseC_XXX() and ParseActC_XXX(). It provides a similar

    role to oParserVerb for commands. More rule sets are also

    added by calling AIConversationParser() for each of the factions

    that the AI is a member of, allowing the AI to "know" information

    specific to the faction. Since AIConversationParser() is

    layerable, sub-classes of the AI, such as cMerchant, can

    also provide extra rule sets.

</p>

 

<p>

    When AIConversationParser() is called in an object, it does

    the following:

</p>

 

<ol>

    <li>

           <bold>Identify the rule sets</bold> it wishes to activate,

           such as <bold>rParserCommon</bold> from above.

    </li>

    <li>

           <bold>See if the rule set is already loaded into the parser</bold> by

           calling <bold>NLPRuleSetExists()</bold>.

    </li>

    <p>

           (In order to prevent

           resources from piling up, rule sets will be deleted from time to

           time, so it's always a good idea to make sure the rule set

           still exists, even if you're sure it was created for the last

           conversation.)

    </p>

    <li>

           If the rule set exists, the method should

           call <bold>NLPRuleSetEnableSet()</bold> to make sure the rule

           set is active.

    </li>

    <p>

           Before PerceiveConversation() calls the AIConversationParser()

           methods, it will have <bold>disabled all of the rule

           sets</bold> in gConversationParser.

    </p>

    <li>

           If the rule set does not exist, create it and

           call <bold>NLPRuleSetAdd()</bold>, followed

           by <bold>NLPRuleSetEnableSet()</bold>.

    </li>

    <li>

           If the object provides any ParserC_XXX() methods, which

           it probably does, then <bold>add the object to the ParserObjects

           list</bold> that's passed into the method. (See below.)

    </li>

</ol>

 

 

<br/><section><p><bold><big>Finding the best parse</big></bold></p></section>

 

<p>

    Once AIConversationParser() has been called, and all the rule

    sets have been added, PerceiveConversation()

    calls <bold>NLPParse()</bold> to produce a list of possible

    parses for what the player said.

</p>

 

<p>

    The <bold>first word</bold> of each hypothesis is examined:

</p>

 

<p>

    If it begins with a "`", then ParseC_XXX() is called for each

    of the objects added to ParserObjects by AIConversationParse().

    XXX is the string after the "`". Thus, if the character said,

    "My name is Fred", one of the hypothesis would be "`identifyself Fred".

    This would cause ParseC_IdentifySelf() to be called for

    each of the objects in ParserObjects. (More about this later.)

</p>

 

<p>

    If the first word has no "`", then the method(s) called are

    determined by the current conversation state.

    (See <bold>AIConversationStateSet()</bold>). The method

    used will be "ParseC_State_XXX", where XXX is replaced with

    the state name. Thus, if the NPC asked the player, "What is your

    name?", it could set the converstion state to "AskedName". Then,

    if the player replied, "My name is Fred", this would result in

    a "`identifyself Fred" parse, that would call ParseC_IdentifySelf().

    However, if the user replied with "Fred", the first word

    of the parse wouldn't have a "`", so ParseC_State_AskedName() would

    be called. (If the NPC had asked for the PC's favorite color,

    it could use the "AskFavoriteColor" state, so as to not

    accidentally think that the PC's name was "red" or that his

    favorite color was "Fred".)

</p>

 

<p>

    The ParseC_XXX() methods accept the following parameters:

</p>

 

<ul>

    <li>

           <bold>Actor</bold> - The AI being spoken to.

    </li>

    <li>

           <bold>Speaker</bold> - The character doing the speaking.

    </li>

    <li>

           <bold>CommandTokens</bold> - A list of words spoken. Some of the

           words may be converted to objects. Thus, if the user said,

           "I like your lantern", this might be ["`ilike", oLantern],

           whereas "My name is Fred" would be ["`identifyself", "Fred"].

    </li>

    <li>

           <bold>BestParse</bold> - This is the best parse for the CommandTokens

           so far. If the ParseC_XXX() method comes up with a higher score,

           it should overwrite the elements of BestParse(). See below.

    </li>

    <li>

           <bold><italic>Returns</italic></bold> - None.

    </li>

</ul>

 

<p>

    The ParseX_XXX() method does the following:

</p>

 

<ol>

    <li>

           It <bold>examines the CommandTokens</bold> to see if can

           make any sense of them.

    </li>

    <li>

           If it <bold>can't make any sense of the tokens</bold> then

           the method should just return without changing BestParse().

    </li>

    <li>

           If the tokens do make sense, then the method needs

           to <bold>calculate a score</bold> for how much they make sense.

           Use 1.0 for a statement that makes perfect sense,

           such as ["`identifyself", "Fred"]. If it makes less sense,

           such as ["`identifyself", "a", "new", "person", "here"] then

           return a lower value, like 0.5. If it makes very little

           sense then use an even lower number.

    </li>

    <li>

           If the <bold>score is more than BestParse[0]</bold>, then you'll need to

           replace the contents of BestParse. If it isn't then

           just return. (BestParse[0] will be Undefined if there hasn't

           been a good parse yet.)

    </li>

    <li>

           Replace <bold>BestParse[0] with the score</bold> you calculated.

    </li>

    <li>

           <bold>BestParse[1] should contain the conversation state(s)</bold> that

           this response is valid for. This is either a single lower-case string,

           or a list of lower-case strings. They can contain

           wildcards. (See <bold>ConversationStateCompare()</bold>.)

    </li>

    <p>

           If the conversation states in BestParse[1] do <bold>not</bold> match

           any of the current conversation state the the AI is listening for

           then the score will be automatically reduced. This way, if

           a player's response is ambiguous, the AI can use the expected

           conversation state to disambiguate. Example: If the AI asks the

           player a question and the player answers, "Yes, I do", resulting

           in in ["`answeryes"], the BestParse[1] state will allow the

           AI to determine if the "Yes" belonged to "Do you like me?"

           ("askiflikeme" conversation state) or "Do you like eating turkey?"

           ("askifliketurkey" conversation state).

    </p>

    <p>

           Furthermore, if the player responds with a question or answer

           that isn't in the AI's current conversation state, this will

           annoy the AI slightly and reduce the AI's like/trust for

           the PC. A change in topic is determined by comparing the

           BestParse[1] state with the AI's converastion state

           set in <bold>AIConversationStateSet()</bold>.

    </p>

    <li>

           <bold>BestParse[2] should be filled with a callback

           that acts on the speech</bold>. Usually, this will

           be "this.ParseActC_XXX", where XXX is the first word

           in the parse, such as "this.ParseActC_IdentifySelf".

    </li>

    <li>

           <bold>BestParse[3] and higher</bold> are passed into

           the BestParse[2] callback. Their meaning is up to the

           BestParse[2] callback.

    </li>

</ol>

 

 

<p>

    After all the ParseC_XXX() methods have been called, the

    one that filled in <bold>the highest BestParse[0] will be kept</bold>.

    NOTE: The values of BestParse[0] are adjusted down if

    the conversation states in BestParse[1] do not match

    the AI's current conversation state,

    from <bold>AIConversationStateGet()</bold>. The BestParse[0]

    score is also multiplied by the score produced by

    the rule-set parsing.

</p>

 

 

<br/><section><p><bold><big>Responding to the parse</big></bold></p></section>

 

<p>

    If no parse was successful, then <bold>AIConversationDontUnderstand()</bold> is

    called. This will cause the AI to answer with,

    "Sorry, I don't understand.", or something to the effect.

</p>

 

<p>

    Otherwise, the callback from BestParse[2] will be called.

    It should accept the following parameters:

</p>

 

<ul>

    <li>

           <bold>Actor</bold> - The AI being spoken to.

    </li>

    <li>

           <bold>Speaker</bold> - The character doing the speaker.

    </li>

    <li>

           <bold>ParseList</bold> - The values from BestParse[3] and

           higher. The meaning depends upon how the method wishes

           to interpret them.

    </li>

    <li>

           <bold><italic>Returns</italic></bold> - See below.

    </li>

</ul>

 

<p>

    The ParseActC_XXX() method should do the following:

</p>

 

<ol>

    <li>

           <bold>Have the NPC speak or act</bold> based on how it

           interprets the speech.

    </li>

    <li>

           The NPC might wish to call <bold>AILikeSet()</bold> if

           what the player says causes the NPC to change his opinion

           of the player.

    </li>

    <p>

           Tip: If something that the player says positively

           impaces the AI's view of the PC, make sure to use AIMemoryGet() and

           AIMemorySet() to remember the event. Otherwise, players will discover

           that every time the compliment a NPC about their hair, the

           NPC's like/trust will go up. You'll find players repeated complimenting

           a NPC about its hair until the NPC is super-friendly.

    </p>

    <li>

           If the conversation sentence mentions an object that might

           be used as a pronoun later, make sure to

           call <bold>AIConversationPronounSet()</bold>.

    </li>

    <li>

           If the AI's converation state won't change based on the sentence,

           then <bold>return NULL</bold>. This is particularly useful

           for emotes, since many times an emote will just be ignored

           by the AI, and it will sit waiting for some speech.

    </li>

    <li>

           If the <bold>AI always responds to this parse with

           a question,</bold> then <bold>code the question</bold> into this method and

           call <bold>AIConversationStateSet()</bold> to set the new state.

           Then, <bold>return TRUE</bold>.

    </li>

    <p>

           Example: If this method handles the player asking, "Where is

           the cave of Wigeywom?" and the NPC's reaction to this is

           always, "Why do you want to know?", then this response should

           be coded in, and the new conversation state should be set to

           "npc_asks_why_want_to_know_wigeywom".

    </p>

    <li>

           If the AI has <bold>no particular follow-up response</bold> then

           return the new conversation state. This can either be a single

           lower-case string, or list of lower-case strings. Normally,

           the like/trust penalty for the PC jumping to a different

           topic is [-0.5, 0]. If you wish a different pentaly, then

           return a list of states, but set the first element of the

           list to your [Like, Trust] change.

    </li>

    <p>

           For example: The NPC asks the PC, "What's your name?". This

           method interpret's the player's answer of "My name is Fred."

           Since nothing logically follows, returning a new state

           will allow the AI's intelligence to select a new topic

           of conversation by calling <bold>AIConversationQuestionAsk()</bold>.

           See below for details.

    </p>

</ol>

 

 

<br/><section><p><bold><big>Conversation questions</big></bold></p></section>

 

<p>

    Conversations are not just an interrogation where the player

    asks the NPC a series of questions and the NPC dutifully answers.

    NPCs have their own agendas... When a conversation is first

    begun, the NPC AI code initializes a list of "questions" that the

    NPC wants to ask the PC.

</p>

 

<p>

    These "questions" can also include statements. Some examples

    of questions are:

</p>

 

<ul>

    <li>

           <bold>Greeting</bold> - When a conversation first starts the

           NPC will say "Hello" (or similar) to the PC.

    </li>

    <li>

           <bold>What is your name?</bold> - If the player has not

           given his name, the NPC will ask for it. (Depending upon the

           NPC's personality.)

    </li>

    <li>

           <bold>My name is X.</bold> - The NPC may wish to identify itself

           to the player.

    </li>

    <li>

           <bold>Rumors</bold> - If there's a hot rumor going around,

           or even just the latest sports trivia, the NPC may find a good

           opportunity to relate it.

    </li>

    <li>

           <bold>Friendly questions</bold> - The NPC may ask some friendly

           questions, such as, "Where do you come from?", "Do you have

           any family nearby?", etc.

    </li>

    <li>

           <bold>Interrogating questions</bold> - The NPC may wish to learn

           more about the player, and might have questions to ask

           like, "To what duke does your allegiance lie?".

    </li>

</ul>

 

<p>

    To programatically add a question to the NPC's list,

    call <bold>AIConversationQuestionAdd()</bold>. You'll need to

    pass in a few parameters. Some parameters to note are:

</p>

 

<ul>

    <li>

           <bold>Priority</bold> - Higher priority questions are more likely

           to be asked.

    </li>

    <li>

           <bold>ConversationState</bold> - The NPC will try to bring

           up questions that are appropriate to the current conversation

           state. If the player brings up fishing, the AI is more likely

           to mention the latest rumor about the giant turtle in the lake.

           If the conversation states don't match then the effective

           priority will be about 1/4 of the priority value.

    </li>

    <li>

           <bold>Callback or speak string</bold> - If the question is chosen

           to be spoken, then a callback will be called. (See below.) If

           you don't need a callback, then just pass in a string to be

           spoken.

    </li>

    <p>

           The callback accepts the following parameters:

    </p>

    <ul>

           <li>

                  <bold>Actor</bold> - NPC/AI object.

           </li>

           <li>

                  <bold>Speaker</bold> - Character (PC) that the AI is speaking to.

           </li>

           <li>

                  <bold>Parameter</bold> - A parameter passed in

                  when AIConversationQuestionAdd() is called.

           </li>

           <li>

                  <bold><italic>Returns</italic></bold> - The callback needs to

                  return the new conversation state(s) to use after the

                  question (or rumor) has been spoken. This is either a lower-case

                  string, or a list of lower case strings. Normally, the non-sequiter

                  penality for [Like, Trust] is [-0.5, 0]. If you wish to change this,

                  then return a list of conversation states, with the first element

                  being the [Like, Truest] change for a non-sequiter.

           </li>

    </ul>

</ul>

 

<p>

    <bold>You may wish to call AIConversationQuestionAdd() in some of

    the ParseActC_XXX() methods</bold> if the player speaks something

    that trigger's the NPC's memory or curiosity. For example: If the

    player mentions "The order of the rising baloon", the NPC may

    become suspicious of the PC and wish to ask questions that

    allay or verify the suspicions. One way to do this would be

    to use AIConversationQuestionAdd() to add a number of questions

    about "The order of the rising baloon".

</p>

 

<p>

    You can also call <bold>AIConversationQuestionRemove()</bold> to

    remove a question from the list.

</p>

 

<p>

    Most questions are added in

    the <bold>AIConversationQuestionFill()</bold> method. This method

    (part of the cAI class) is called whenever a conversation is

    started up. It is passed +1 is the AI initiates the conversation,

    and -1 if the PC initiates the conversation. (It's also called

    if all the AI's questions have been used up and it needs new ones.

    A value if 0 is then passed in.)

</p>

 

<p>

    The default implimenation of AIConversationQuestionFill() calls

    the same method in all the AI's factions, as well as those

    objects listed in pAIConversationParser. It also calls

    oParserConversation.AIConversationQuestionFill(). The method

    is layerable, so sub-classes can include their own questions.

    For example: You could have a cMobileLikesToTellJokes that would

    occasionally tell a joke using AIConversationQuestionFill().

</p>

 

<p>

    oParserConveration's version of AIConversationQuestionFill() will

    set up some default "questions" that are common to all NPCs.

    This includes a "question" that causes the NPC to say "Hello" (or

    similar) and ask the player's name (depending on the AI's

    personality properties).

</p>

 

<p>

    The added <bold>questions are used when a ParseActC_XXX() method

    returns a conversation state or list of conversation states.</bold> (But

    not if the callback returns NULL or TRUE.)

    To select a question to ask, <bold>AIConversationQuestionAsk()</bold> is

    called.

</p>

 

<p>

    This method finds the question with the highest score,

    given the current conversation state. If the score is less than

    a value calculated from the

    AI's <bold>pAIConversationTalkativeness</bold> personality property

    then the AI won't say anything. (You might be able to make the

    talkativeness property for some AIs vary depending upon how

    drunk they are.) Otherwise, the AI will remove the question from

    the list (so it's not spoken again) and then execute the callback,

    or speak the string.

</p>

 

<p>

    In some cases, <bold>automatically removing the question may cause

    some problems for a conversation</bold>. For example: If the AI asks

    the PC for his name, but the PC evades the answer, the AI won't

    ask for the PC's name until the next conversation. You may want

    some AIs to keep asking the player character's name until the AI gets

    an answer. To do this, you can play the following trick:

</p>

 

<ol>

    <li>

           Have the question callback clear the list of questions

           by calling <bold>AIConversationQuestionRemove(Actor, NULL)</bold>.

    </li>

    <li>

           The next time the AI has the opportunity to ask a question,

           it will realize that there are no questions left. This

           will cause it to call <bold>AIConversationQuestionFill()</bold>.

    </li>

    <li>

           Have the AI's AIConversationQuestionFill() code test to make

           sure that the question was answered. <bold>If it wasn't, add it

           back in with a very high priority</bold> (such as 4.0).

    </li>

</ol>

 

 

 

<br/><section><p><bold><big>Built-in parsers and questions</big></bold></p></section>

 

<p>

    oParserConversation includes many built-in parsers and questions

    that the AI will ask. Some of them include:

</p>

 

<ul>

    <li>

           <bold>ParseC_AskAIName</bold> allows the player to ask the

           AI its name.

    </li>

    <li>

           <bold>ParseC_Emote</bold> deals with emotes from the player.

    </li>

    <li>

           <bold>ParseC_Goodbye</bold> lets the player say goodbye to the NPC.

    </li>

    <li>

           <bold>ParseC_Hello</bold> allows the player to say hello.

    </li>

    <li>

           <bold>ParseC_IdentifySelf</bold> is called when the player

           identifies himself, so says, "My name is XXX."

    </li>

    <li>

           <bold>ParseC_State_NameOnly</bold> is activated when the AI

           asks the player his name. This allows a one or two word response.

    </li>

    <li>

           <bold>QuestionC_AskPCName</bold> causes the AI to ask

           the player's name.

    </li>

    <li>

           <bold>QuestionC_GoAway</bold> tells the player to go away

           and ends the conversation.

    </li>

    <li>

           <bold>QuestionC_Goodbye</bold> causes the AI to say

           goodbye and ends the conversation.

    </li>

    <li>

           <bold>QuestionC_Hello</bold> lets the AI say hello.

    </li>

    <li>

           <bold>QuestionC_SayName</bold> causes the AI to say

           it's name.

    </li>

</ul>

 

 

 

 

<br/><section><p><bold><big>Odds and ends</big></bold></p></section>

 

<ul>

    <li>

           The <bold>"anything"</bold> conversation state is used when

           the AI is expecting any (normal) topic. The AI will expect this

           state after the AI and PC have said hellos.

    </li>

</ul>

12 Factoids – cFactoid

Factoids make it easy to create a knowledge base for NPCs.

 

<section><p><bold><big>Factoids - cFactoids</big></bold></p></section>

 

 

<p>

    TIP: There are <bold>easier ways</bold> for NPCs to answer questions.

    See pAIQuestionsAndAnswers, or AIListenForEnum(), AIListenForIsValid(),

    and AIListenForAct().

</p>

 

<p>

    Factoids, based on <bold>cFactoid</bold>, make it easy to create a knowledge

    base for NPCs. A factoid is an object that stores a question or

    statement that the user speaks, such as "Where does King Rehald live?",

    along with a response, "Kind Rehald lives in Burandon." Factoids actually store

    a lot more information than the question and response, but those

    are the basics.

</p>

 

<p>

    Each faction (cFaction) maintains a list of factoid objects

    known by the faction, and which are ultimately known

    by the individual members of the faction.

    Individuals can also have their own list of factoids, outside

    of what they know from any factions they belong to.

</p>

 

 

 

 

<br/><section><p><bold><big>Creating a factoid</big></bold></p></section>

 

<p>

    To create a factoid that handles a simple question and answer:

</p>

 

<ol>

    <li>

           <bold>Create a new object</bold> based on <bold>cFactoid</bold>. The

           object's parent/container should be NULL.

    </li>

    <li>

           Fill in <bold>pFactoidParse</bold> with the parse string

           that causes the factoid to be spoken... This is the phrase

           that must be spoken by the player.

    </li>

    <p>

           At its simplest, pFactoidParse is something like,

           ["Where does King Rehald live?"]. However, this requires that

           the player types in an exact string and doesn't take full

           advantage of the parser's abilities.

    </p>

    <p>

           A better solution would be ["`where", oKingRehald], which

           can take advantage of a provided global and become,

           [gFactionWhere, oKingRehald]. The rules in rParserConversation

           convert "Where X?" questions into "`where X". By using

           oKingRehald instead of "King Rehald", the parser interpreting

           the factoid not only knows which object is being referred to,

           but it knows all the object's parse names (from pNameRealParse),

           so the user would be able to type "King Rehald", "Lord

           Rehald", "Sirius Rehald IV", etc.

    </p>

    <p>

           pFactoidParse supports multiple sentences too. For more information

           see the documentation.

    </p>

    <li>

           <bold>pFactoidSpeak</bold> needs to be filled in with the NPC's

           response to the question, such as ["Kind Rehald lives in Burandon."].

    </li>

    <p>

           pFactoidSpeak allows the NPC to do more than just speak. It can

           cause it to whisper, yell, emote, or have the narrator speak.

           For more information see the documentation.

    </p>

    <li>

           For each faction that would know this information,

           fill in the faction's <bold>pAIFactoids</bold>.

           Obviously, any

           subject of King Rehald would know this tidbit, as well as

           neighbors and educated NPCs around the world. Example:

           oFactionResidentOfBurandon.pAIFactoids = [ThisFactoid]; as

           well as oFactionScholar.pAIFactoids = [ThisFactoid].

    </li>

    <p>

           Alternatively, <bold>if you wish a NPC to know about a factoid</bold> independent

           of the factions it belongs to, you need to change the

           NPC's <bold>pAIFactoids</bold> to include the factoid object.

    </p>

</ol>

 

<p>

    That's all there is to factoids, at the basic level at least.

    The only problem with them is that you'll need to create an

    awful lot of factoids for a world populated with NPCs.

</p>

 

 

<br/><section><p><bold><big>Advanced factoid parsing</big></bold></p></section>

 

<p>

    Factoids can handle more sophisticated parsing that what

    I descibed:

</p>

 

<ul>

    <li>

           <bold>pFactoidParse</bold> can contain several different

           parses, as I already mentioned. To do this, create a list

           of parses, each parse being a sub-list. For example: [ [gFactoidWhere,

           oKingRehald], ["where does", oKingRehald, "(live|reign)"] ].

           You can even append a probability to each parse, just like

           you set a probability in rule set resources.

    </li>

    <li>

           There are several globals for who, what, why, where, how, etc.

           with the names <bold>gFactoidXXX</bold>, where XXX is replaced

           by "Who", etc. I suggest using to the globals to minimize

           typing mistakes that will be difficult to find.

    </li>

    <li>

           If you need to define additional rules that do not relate

           immediately to the phrase, you can do so by

           setting <bold>pFactoidParseSyn</bold>. For example, if

           you wished to convert "the lord of the land" to "King Rehald"

           then set pFactoidParseReword to [ ["the lord of the land",

           "King Rehald"] ]

    </li>

    <li>

           <bold>pFactoidParseReword</bold> is similar except that it

           rewords an entire sentence.

    </li>

</ul>

 

 

<br/><section><p><bold><big>Advanced factoid speech</big></bold></p></section>

 

<p>

    There are many ways to control what is spoken, and other

    NPC reactions:

</p>

 

<ul>

    <li>

           <bold>pFactoidSpeak</bold> can contain &lt;SpeakScript&gt; resources,

           causing the NPC to whisper, emote, or include narration.

    </li>

    <li>

           You can provide different versions of what gets spoken depending

           upon the NPC's rank within the faction from which he inhereted

           the factoid. This lets the common folk have one version of

           events, while nobility might have another. To do this, create

           a list with sub-lists for each of the alternative phrases.

           Prefix each sub-list by the rank that the NPC needs to be in

           order to speak that version of events.

    </li>

    <p>

           Example: [ ["The king lives up in the palace."], [5, "King

           Rehald reigns from the east wing of the Silver Palance."] ].

           You don't need a number for the first sub-string since there

           is no minimum rank. Any NPC up to rank 5 will speak the first

           phrase. Any one with 5 or above will speak the second.

    </p>

    <li>

           <bold>pConvElementConversationState</bold> controls what the conversation

           state will be <bold>after</bold> the factoid has been spoken.

           If you leave this undefined then the state will be set based

           upon whatever objects can be found in pFactoidParse and

           pFactoidPronoun. It also affects what the conversation state

           the factoid will be brought up in.

    </li>

    <li>

           <bold>pFactoidEmotionsSet</bold> will change the NPC's emotions

           when they speak the factoid. For example: Asking a mother about

           her lost child might make her sad.

    </li>

    <li>

           <bold>pFactoidLikeSet</bold> affects how much the NPC's like/trust

           of the PC changes after the PC asks the question. For example:

           A slanderous comment about King Rehald might go over well with

           some factions, but poorly with others.

    </li>

    <li>

           <bold>pFactoidMemorySet</bold> can be used to set a

           memory in the faction's or NPC's memory (using AIMemorySet()).

    </li>

    <p>

           NOTE: Every time a NPC speaks a factoid, it remembers that it

           spoke the factoid. The system does this to ensure that NPCs

           don't speak the same factoid over and over, particularly

           for questions. (See below.)

    </p>

    <li>

           When factoids are spoken they try to keep track of pronoun

           meanings just in case the player uses "he" or "it" in the

           next sentence. If <bold>pFactoidPronoun</bold> has any objects,

           they are used for pronouns. If the list is empty,

           pronouns are derived from any objects listed

           in pFactoidParse.

    </li>

    <li>

           <bold>pFactoidConvTree</bold> will cause a conversation tree

           to be started immediately after the factoid has spoken.

           For more information about conversation trees, see the

           cConvTree tutorial.

    </li>

    <li>

           <bold>pFactoidQuestionFill</bold> lets the NPC's speaking of

           a factoid spur on some other memories/comments that it

           will add to the conversation if given a chance. Just fill

           pFactoidQuestionFill with a single factoid object, or a list

           of factoid objects. See below for more information about

           questions.

    </li>

    <li>

           Some factoids allow the NPC to change the conversation's

           direction by using a question from the question list, while

           other factoids require that the NPC wait for the player

           to speak next. For example: If a factoid were, "The King lives

           up in the palace.", the NPC might append a question/statement from

           the question list, such as "I visited the palace once.". However, if

           the factoid ended in a question then NPC would have to

           shut up and wait for the PC to respond, such as "Do you want

           to know where the king lives in the summer or winter?".

           To prevent the NPC from asking a follow-on question,

           set <bold>pFactoidWaitForResponse</bold> to TRUE. Leave it

           undefined to allow a follow-on question.

    </li>

    <li>

           If these properties don't meet your requirements for the

           factoid, you can always write your

           own <bold>FactoidAct()</bold> method, which is called

           when the factoid is to be spoken.

    </li>

</ul>

 

 

<br/><section><p><bold><big>Advanced factoid limits</big></bold></p></section>

 

<p>

    Even though a NPC is a member of a faction that knows a factoid,

    the individual NPC may not know the factoid. Or, the NPC may

    not be willing to divulge the factoid to the PC:

</p>

 

<ul>

    <li>

           <bold>pFactoidSpeak</bold> can be used to limit the factoid's

           knowledge to faction members of a specific rank. [ [5, "The king's

           first name is Alendre."] ], limits the factoid to only

           NPCs with a 5 or higher ranking in their faction. [ ["The emperor

           has no clothes.", [4] ], causes all the peasants to realize that

           the emperor has no clothes, but anyone with a 4 or higher rank

           won't know the factoid.

    </li>

    <li>

           <bold>pFactoidEmotionsRequired</bold> will cause the NPC to

           only speak the factoid when he/she is very happy, very sad,

           etc.

    </li>

    <li>

           <bold>pFactoidFactionsRequired</bold> ensures that the NPC

           will only speak the factoid to PCs who are a member of a specific

           faction. (Or not a member.)

    </li>

    <li>

           <bold>pFactoidHoldRequired</bold> will prevent the NPC from

           speaking the factoid unless the PC is holding a specific object,

           such as a piece of jewelry that idenfifies the PC as a friend.

           (Or the reverse.)

    </li>

    <li>

           <bold>pFactoidLikeRequired</bold> causes the NPC to only

           speak the factoid if the NPC's like/trust for the PC is within

           a given range.

    </li>

    <li>

           <bold>pFactoidMemoryRequired</bold> tests a memory of the

           NPC or the NPC's faction (see AIMemoryGet()) and uses it

           to determine if the factoid will be revealed. For example: If

           the PC hasn't declared allegiance to the king (which caused

           a memory to be stored), then he won't be told about many factoids.

    </li>

    <li>

           <bold>pFactoidRaceRequired</bold> will only let the NPC

           speak a factoid if the PC is of a specific race. (Or not

           of a specific race.)

    </li>

    <li>

           If you wish to impose other tests, you can write

           your own <bold>FactoidValid()</bold> method with your

           own test. For example: You may wish a factoid discussing

           a rumor about a werewolf to be spoken in the week preceding

           a full moon.

    </li>

</ul>

 

<br/><section><p><bold><big>Using factoids as conversation questions</big></bold></p></section>

 

<p>

    Factoids can also be used for conversation questions that

    are added using AIConversationQuestionAdd(). There

    are two ways to add a factoid to the list of questions in

    a conversation:

</p>

 

<ol>

    <li>

           If the NPC speaks on factoid, that may bring up other

           memories/thoughts that is will express later, using

           the question mechanism. To do this, set

           the factoid's <bold>pFactoidQuestions</bold> to the questions/factoids

           that will come to the NPC's mind.

    </li>

    <li>

           If a factoid has <bold>pFactoidQuestionFill</bold> set then

           it will automatically be added as a question when the conversation

           begins.

    </li>

</ol>

 

<p>

    <bold>In both cases</bold>, factoids will <bold>not</bold> be

    added as factoids if the fail to meet the critera for

    the factoid (such as pFactoidLikeRequired), or the factoid

    was already spoken to the PC by the NPC.

</p>

 

<p>

    You can set the question priority by

    using <bold>pConvElementPriority</bold>. As

    with an question, the priority

    is also affected by how well the factoid's state matches

    the current conversation state. The state

    is set using <bold>pConvElementConversationState</bold>. If

    left blank, a state will be generated from any objects

    listed in pFactoidParse and pFactoidPronoun.

</p>

13 Knowledge and rumors – cKnowledge

Factoids make it easy to create a knowledge base for NPCs.

 

<section><p><bold><big>Knowledge and rumors - cKnowledge</big></bold></p></section>

 

 

<p>

    As players wander around the world, their characters will learn various

    bits of information, such as rumors ("The mayor is a member of an evil

    witchcraft organization.") or facts that allow them access to

    new maps ("Farmer John's farm is over yon hill.")

</p>

 

<p>

    These facts are represented by <bold>cKnowledge</bold>-based objects,

    much like factoids (based on cFactoid). In fact, you

    can <bold>combine a cFactoid and cKnowlege</bold> object, so that

    when a NPC speaks the factoid, the PC automatically remembers it

    as a piece of knowledge. Or, you can <bold>combine a cConvStory and

    cKnowledge</bold> object, causing the knowledge to be automatically

    learned when the player hears a story.

    If you do this, make sure <bold>cKnowledge is lower on the class list</bold> than cConvStory

    or cFactoid.

</p>

 

<p>

    During gameplay, the player can see what knowledge their character

    knows by typing, "What do I know". They can also tell the knowledge to NPCs,

    as well as telling NPCs the source of the knowledge... A witch hunter

    might be interested in knowing that the mayor is a witch, but the mayor

    would be more interested in knowing who told the player that he was

    a witch.

</p>

 

 

<br/><section><p><bold><big>Creating a knowledge object</big></bold></p></section>

 

<p>

    To create a knowlege object:

</p>

 

<ol>

    <li>

           Create an object, as normal, but base it off

           of <bold>cKnowledge</bold>.

    </li>

    <p>

           If you want the knowledge to automatically

           be imparted to the PC when a NPC speaks a factoid, then also

           base the object off of <bold>cFactoid</bold> or <bold>cConvStory</bold>,

           and follow the factoid or story

           creation process, as well as this one. Make sure the cKnowledge class

           is lower priority than the cFactoid or cConvStory class.

    </p>

    <li>

           The object should <bold>not be contained</bold> in anything.

    </li>

    <li>

           Fill in <bold>pKnowledgeDescription</bold> with a description of the

           knowledge as it will appear in the player's list. For example: "The

           mayor is a member if a witchcraft cult."

    </li>

    <li>

           Fill in <bold>pKnowledgeCategory</bold> with a string for the

           category that the knowledge will appear under, such as "Mayor Daily".

    </li>

    <li>

           <bold>pConvElementConversationState</bold> should be filled in with

           a list of topics (lower-case string) describing the knowledge. This

           is very important for when a player and NPC go back and forth

           swapping stories and rumors.

    </li>

</ol>

 

<p>

    You might also wish to fill in the following:

</p>

 

<ul>

    <li>

           <bold>pIsInvisible</bold> will prevent the knowledge from being

           displayed on the player's list, as well as prevent the player from speaking

           the knowledge to NPCs. This is a useful way to ensure that the player hears

           something only once, but which isn't important enough to see on the

           player's knowledge list.

    </li>

    <li>

           <bold>pKnowledgeCanRetell</bold> defaults to TRUE indicating that the player

           can retell the knowledge to NPCs. If the knowledge isn't story-worthy,

           then set this to FALSE.

    </li>

    <li>

           <bold>pKnowledgeEmotions</bold> specifies how the NPC's emotions (happy/sad,

           angry/afraid) will be affected when they hear the knowledge.

    </li>

    <li>

           <bold>pKnowledgeLike</bold> specifies how the NPC's like/trust towards

           the player will change when they hear the knowledge.

    </li>

    <li>

           <bold>pKnowledgeSpeak</bold> can contain the string that

           the player will speak when they reveal the knowledge to a NPC.

           If you don't provide this the a sentence will automatically be

           genereated from pKnowledgeDescription.

           Example: "Did you know that Mayor Daily is a witch?"

    </li>

    <li>

           <bold>pKnowledgeSpeakFromWhom</bold> is used if the

           player mentions their source.

           If you don't provide this the a sentence will automatically be

           genereated from pKnowledgeDescription.

           Example: "%1 (told/told/told) me that Mayor Daily

           is a witch."

    </li>

    <li>

           The <bold>KnowledgeSpeak()</bold> method generates the sentence

           that the player speaks. Although unlikely, you might wish to

           write your own method.

    </li>

    <li>

           <bold>pKnowledgeResponseDontCare</bold> is filled with

           a NPC's response if they don't care about the rumor. If not

           filled in, a default response will be used.

           Example: "Don't spread such rumors! You'll get in trouble."

    </li>

    <li>

           <bold>pKnowledgeConversationStateExtreme</bold> affects how

           much this knowledge affects "sensative" NPCs who list a corresponding

           conversation-state topic in <bold>pAIKnowledgeConversationStateExtreme</bold>.

           For example: If this is a sad story about a child's death, then NPCs

           hearing it will be saddened. However, if the NPC happens to be the child's

           parent, then there will be an extra effect.

    </li>

    <li>

           <bold>pKnowledgeDeduce</bold> causes a new piece of knowledge to be

           automatically deduced if the player learns two or more other

           pieces of knowledge. This is useful for a detective title.

    </li>

    <li>

           <bold>pKnowledgeAboutRelationships</bold> affects what relationship

           connections are learned when the player character hears the knowledge.

    </li>

    <li>

           <bold>pKnowledgeMystery</bold> causes the player's list of mysteries

           to be added to when this knowledge object is added.

    </li>

</ul>

 

 

<br/><section><p><bold><big>Learning knowledge</big></bold></p></section>

 

<p>

    If you wish a player character to know about a knowledge object,

    call <bold>Actor.KnowledgeAdd()</bold>. Alternatively, if

    you <bold>combine a cKnowledge and cFactoid or cConvStory</bold> object, then the

    knowledge will automatically be learned when a NPC speaks the factoid/story.

</p>

 

<p>

    You can remove a knowledge object by

    calling <bold>KnowledgeRemove()</bold>. <bold>KnowledgeQuery()</bold> tests

    to see if a character knows about the object.

</p>

 

 

 

 

<br/><section><p><bold><big>Knowledge affecting access</big></bold></p></section>

 

<p>

    If you look under the <bold>cMap</bold> tutorial, you'll see that a

    knowledge object can be used to determine if a player can travel to a given

    map. Thus, if the player knows where "John's farm" is, with a cKnowledgeJohnsFarm

    object, then they can get to it.

</p>

 

 

 

 

<br/><section><p><bold><big>Telling NPCs knowledge in the form of stories and rumors</big></bold></p></section>

 

 

<p>

    Players can tell NPCs knowledge they know. Most NPCs won't care about

    the knowledge, but some NPCs will react positively, as in the case of

    a witch hunter and the mayor. Or, a piece of knowledge could even be

    a good joke that the player heard.

</p>

 

<p>

    NPCs all have general (and automatic) responses to being told

    knowledge. This reaction is affected by:

</p>

 

<ul>

    <li>

           If the NPC already knows the knowledge, or if the player has already

           told them it, they may get peeved with the player.

           The method, <bold>AIKnowledgeKnow()</bold> is used to determine if

           the NPC already knows the object.

    </li>

    <li>

           The more the NPC's <bold>pAIConvStoryPreferred</bold> correlates with

           the knowledge's <bold>pConvElementConversationState</bold>, the

           more effect that speaking the knowledge will have. If there's a negative

           correlation due to oppostes (ConversationStateOpposites()) then

           the effects might be reversed.

    </li>

    <li>

           If a NPC has <bold>pAIKnowledgeConversationStateExtreme</bold>, and

           one or more of the conversation-state topics is in common with the

           knowlege object's pConvElementConversationState, then the NPC

           may have a much stronger reaction.

    </li>

    <li>

           The NPC's speech response will be controlled by a call

           to <bold>cKnowledge.KnowledgeGenericResponse()</bold>. This method

           returns a response based on how positive or negative the NPC's

           reaction is, and what elements of the knowledge produced the

           positive or negative response.

    </li>

    <p>

           Having the NPC specfically state why they liked/disliked the knowledge

           is important feedback to the player, so they can decide what type

           of stories/rumors to tell in the future. You may need to augment

           KnowledgeGenericResponse(), as per the method's documentation, if you

           add new conversation-state topics to the knowledge object's

           pConvElementConversationState. For example: If you create a "airplane"

           topic, which means that the NPC likes hearing and talking about

           airplanes, then you'll need some positive responses if the player's

           stories includes airplanes, such as "I love to fly."

    </p>

    <p>

           Not all NPC's provide regular

           feedback. <bold>pAIKnowledgeSayFeedback</bold> controls how likely it

           is for a NPC to provide feedback about what they liked or disliked.

    </p>

</ul>

 

<p>

    To have a NPC react to a <bold>specific piece of knowledge</bold> that's

    spoken by a player, you have three options:

</p>

 

<ol>

    <li>

           Modify the NPC's <bold>pAIKnowledgeInterest</bold> to list the

           various knowledge objects that interest the NPC, as well as the NPC's

           responses.

    </li>

    <p>

           You can also set a flag in pAIKnowledgeInterest to indicate that

           if the player knows the knowledge, and the NPC hasn't already heard

           it from the player, then a context menu item will appear in the

           NPC's menu; this context menu makes it more obvious to players that

           they should tell the NPC about the knowledge/rumor.

    </p>

    <li>

           Write your own <bold>PerceiveKnowledgeSay()</bold> method for the NPC.

    </li>

    <li>

           If PerceiveKnowledgeSay() doesn't provide enough flexibility,

           you can <bold>write a cFactoid</bold> that is triggered by the player

           speaking "`knowledgesay KNOWLEDGEOBJECT [SOURCEOBJECT]". You probably

           won't have to do this. If you do, make sure you call AIMemoryGet() and

           modify the appropriate flag so the AI know's it has already heard

           the knowledge.

    </li>

</ol>

 

 

 

<br/><section><p><bold><big>Miscellaneous knowledge</big></bold></p></section>

 

<p>

    Some information isn't important enough to be spread as rumors, but

    you still want players to "write it in their journal" for later.

    To add a "journal" entry, call:

</p>

 

<ul>

    <li>

           <bold>KnowledgeMiscAdd()</bold> will add knowledge as a string.

    </li>

    <li>

           <bold>KnowledgeMiscEnum()</bold> finds the list of string knowledge known

           about a NPC.

    </li>

</ul>

 

14 Pre-scripted conversations – cConvScript

How to create pre-scripted conversations between two or more NPCs.

 

<section><p><bold><big>Pre-scripted conversations - cConvScript</big></bold></p></section>

 

 

<p>

    You may wish your NPCs to occasionally have conversations with one

    another. For example: Two friends might bump into one another at the

    fish mongers, or a group of burglers might meet clandestinely the

    night before a burglery to arrange who does what.

</p>

 

<p>

    The easiest way to create a pre-scripted conversation is to

    create an object based off <bold>cConvScript</bold>.

</p>

 

 

<br/><section><p><bold><big>Creating a conversation-script object</big></bold></p></section>

 

<p>

    To create a conversation-script object:

</p>

 

<ol>

    <li>

           Create a <bold>&lt;ConvScript&gt;</bold> resource the scripts

           out the conversation. You'll be able write lines for the NPCs,

           as well as narration, emotes, and sounds.

    </li>

    <li>

           Create an object, as normal, but base it off

           of <bold>cConvScript</bold>. For this example, call

           it oConvScriptMeetAtFishMongers.

    </li>

    <li>

           The object should <bold>not be contained</bold> in anything.

    </li>

    <li>

           Fill in <bold>pConvScriptResource</bold> with the name

           of the resource you created, such as rConvScritMeetAtFishMongers.

    </li>

    <p>

           If you want to generate the conversation on the fly,

           write your own <bold>ConvScriptResource()</bold> method.

    </p>

    <li>

           If your conversation takes place between three or more NPCs, or

           only the NPC, then modify <bold>pConvScriptNPCs</bold> to indicate

           the number of NPCs involved. (The default value is 2.)

    </li>

    <li>

           When the two NPCs meet at the fish mongers,

           call <bold>oConvScriptMeetAtFishMongers.ConvScriptStart()</bold>. That's

           it! The two NPCs will meet and talk about the quality

           of the fish, or whatever.

           (Note: I'll got into an easier way of automatically starting

           conversations later.)

    </li>

</ol>

 

 

<p>

    You may also wish to set the following properties and methods:

</p>

 

<ul>

    <li>

           <bold>pConvScriptAbortIfPCConv</bold>, which defaults to TRUE, causes

           all NPC conversations to automatically stop if a player talks to one

           of the NPCs.

    </li>

    <li>

           <bold>pConvScriptAbortIfUnconscious</bold>, which defaults to TRUE,

           causes the conversation to automatically stop if any of the NPCs

           is unconscious or killed.

    </li>

    <li>

           <bold>pConvScriptSecret</bold> will cause the NPCs to abruptly stop

           the conversation if someone enters the room. They won't even automatically start

           it unless they're alone.

    </li>

    <li>

           <bold>ConvScriptCompleted()</bold> is called when the conversation

           is completed. The default behaviour does nothing, but you could modify this

           if you want your NPCs to act on the conversation. For example,

           the NPCs might talk about the best choice of fish, and one of the NPCs

           might be given the AI goal of buying that particular fish.

    </li>

</ul>

 

 

<br/><section><p><bold><big>Automatic conversation scripts</big></bold></p></section>

 

<p>

    Conversation scripts also provide a mechanism for making them "automatic" so

    that you don't have to constantly check for the right conditions

    and call ConvScriptStart(). Instead, conversation scripts can be set up

    so they are automatically called whenever a NPC enters/leaves a room,

    when a PC enters/leaves a room, or when the NPC is bored.

</p>

 

 

<p>

    To have a NPC speak an automatic conversation script, you need to:

</p>

 

<ol>

    <li>

           Add the conversation script object to the

           NPC's <bold>pAIConvScripts</bold> property.

    </li>

    <p>

           If you want all NPCs in a specific faction to use the conversation

           script, such as a secret greeting to one another, then

           modify the faction's <bold>pAIConvScripts</bold> property.

    </p>

    <p>

           Alternatively, if you want conversation scripts associated with

           certain classes, such as all cRaceElf characters using a type

           of conversation script, modify the <bold>AIConvScripts()</bold> method

           for the class.

    </p>

   

    <li>

           If the conversation requires more than two NPCs,

           it must have <bold>pConvScriptNPCs</bold>. It must also have

           a <bold>ConvScriptAutoPermutations()</bold> written. This method will

           be called with a list of players and NPCs in the room. It must

           return a list of all the "possible" conversations that could result.

           Since there aren't that many possibilities with only two (or one) NPC,

           the method is trivial and automatically handled. However, if you have

           ten NPCs sitting in a room, the number of possible groups of three

           NPCs to sing as a trio is astronomical.

    </li>

   

    <li>

           You need to specify some constraints that limit what two NPCs will

           join together. For example: While all NPCs might have your fish-monger

           conversation at the ready, they'll only use it when they're in

           the fish market. The following properties and methods help limit

           the possibilities.

    </li>

   

    <p>

           <bold>pConvScriptAutoFactions</bold> will limit the conversation so that

           the main NPC (NPC #1) will only use the conversation with NPCs from

           the specified faction(s).

    </p>

   

    <p>

           <bold>pConvScriptAutoNPCs</bold> will limit the conversation so that

           the main NPC (NPC #1) will only use the conversation with NPCs

           listed in pConvScriptAutoNPCs. Example: Applying this to the fish monger

           might mean that Betty would only talk about fish to a handful of

           her friends.

    </p>

   

    <p>

           <bold>pConvScriptAutoListenersExclude</bold> can be used to NOT play

           the conversation if certain NPCs are in the room. This can be used to

           ensures that rumors about NPC X aren't spoken while NPC X is in the room.

    </p>

   

    <p>

           <bold>pConvScriptAutoKnowledge</bold> will only have the conversation

           spoken if the player character <bold>doesn't</bold> know the specified

           knowledge object. You would use this, for example, to have a pair of NPCs

           speak a rumor only once in the precense of a specific

           player. <bold>pConvScriptAutoKnowledgeFlip</bold> inverts the test,

           causing the NPCs to speak the script only if the player has the given

           knowledge.

    </p>

   

    <p>

           <bold>pConvScriptAutoFracture</bold> will only allow the conversation

           script to be spoken when the player is in the given fracture.

           This is passed to IsInDifferentFracture() and FracturerQuery().

    </p>

   

    <p>

           <bold>pConvScriptAutoPriority</bold> controls which automatic

           conversation script has higher priority.

    </p>

   

    <p>

           <bold>pConvScriptAutoRelationship</bold> lets the conversation script

           only happen if a specific relationship exists.

    </p>

   

    <p>

           <bold>pConvScriptAutoRequiresIntelligent</bold> ensures that conversations

           only happen between intelligent creatures.

    </p>

   

    <p>

           These properties won't be enough, though. You'll need

           to write your own <bold>ConvScriptAutoValid()</bold> method for

           many conversation scripts. The method is called whenever a conversation

           script is "proposed" by <bold>ConvScripts()</bold> to see if it

           would work in the given situation.

    </p>

   

    <li>

           You will need to set the liklihood of the conversation happening

           depending upon the circumstances. If you don't set at least one

           of these properties to non-zero then your conversation will never

           be randomly selected.

    </li>

    <p>

           <bold>pConvScriptProbBored</bold> - The probability of the conversation

           script being activated when the NPC is sitting around bored (with

           the oAIGoalBoredConvScript activated). Example: Use this if the NPC

           brings up the "Ever wonder what's up there?" when sitting outside

           on a starry night.

    </p>

    <p>

           <bold>pConvScriptProbNPCEnters</bold> - The probability of the conversation

           script being activated when the NPC enters a room. Example: Use this if the NPC

           says, "Hello everyone!" every time it enters the room.

    </p>

    <p>

           <bold>pConvScriptProbNPC2Enters</bold> - The probability of the conversation

           script being activated when a second NPC enters a room. Example: Use this if the

           first NPC says, "Hi %2!" every time a second NPC enters the room.

    </p>

    <p>

           <bold>pConvScriptProbNPC2Leaves</bold> - The probability of the conversation

           script being activated when a second NPC leaves a room. Example: Use this if the

           first NPC says, "That idiot has finally left." every time a second NPC leaves the room.

    </p>

    <p>

           <bold>pConvScriptProbPCEnters</bold> - The probability of the conversation

           script being activated when a PC enters a room. Example: Use this if the NPC

           says, "Hey, it's the town hero!" every time the PC enters the room.

    </p>

    <p>

           <bold>pConvScriptProbPCLeaves</bold> - The probability of the conversation

           script being activated when a PC leaves a room. Example: Use this if the NPC

           says, "Finally got rid of him!" every time the PC leaves the room.

    </p>

</ol>

 

<p/>

 

<p>

    Some more advanced settings that you may wish to modify:

</p>

 

<ul>

    <li>

           If your conversation requires that a PC be around, perhaps because the

           NPCs are deriding the PC right in front of their face, then

           set <bold>pConvScriptRequiresPC</bold> to TRUE. You <bold>don't</bold> have

           to set this if

           either <bold>pConvScriptAutoKnowledge</bold> or <bold>pConvScriptAutoFracture</bold> are

           set.

    </li>

    <li>

           To randomly start your own conversation,

           call <bold>ConvScriptAutoStart()</bold> at an appropriate time.

    </li>

    <li>

           <bold>gConvScriptRate</bold> controls the default conversation rate. It defaults

           to one act every 3 seconds.

    </li>

</ul>

 

 

 

<br/><section><p><bold><big>Playing conversation scripts through goals</big></bold></p></section>

 

<p>

    If you want a conversation script to take place as part of an AI's goal,

    then use <bold>oAIGoalConvScript</bold> to start the conversation.

</p>

 

15 Conversation trees – cConvTree

How to create pre-scripted conversations between two or more NPCs.

 

<section><p><bold><big>Conversation trees - cConvTree</big></bold></p></section>

 

 

<p>

    Conversation trees are a tride-and-true method for talking to NPCs,

    and the standard NPC conversation system for most CRPGs. For those

    of you not familiar with the term, you'll be familiar with the

    scenario: The NPC says something like "What's your favorite color?", and

    the player is given a menu of choices, such as "Red", "Green", or "Blue".

    Once the player selects the color, the NPC makes a statement and (often)

    a new question is asked by the NPC.

</p>

 

<p>

    While Circumreality doesn't rely on conversation trees (most CRPGs do), it does

    allow for them.

</p>

 

 

<br/><section><p><bold><big>Creating a conversation-tree object</big></bold></p></section>

 

<p>

    To create a conversation tree (combination of question, player answers,

    and NPC's responses to the answers), you need to create a conversation-tree

    object. To create a conversation-tree object:

</p>

 

<ol>

    <li>

           Create an object, as normal, but base it off

           of <bold>cConvTree</bold>. For this example, call

           it oConvTreeFavoriteColor.

    </li>

    <li>

           The object should <bold>not be contained</bold> in anything.

    </li>

    <li>

           Fill <bold>pConvTreeQuestion</bold> with the question that the NPC

           asks, such as "What's your favorite color?"

    </li>

    <li>

           <bold>pConvTreeAnswers</bold> should be filled in with a list of

           answers to the NPC's question, such as ["Red", "Green", "Blue"].

    </li>

    <li>

           <bold>pConvTreeResponses</bold> is a list containing the responses

           to each of the player's answers, such as ["Red? I hate red.",

           "Green is nice.", "Blue is my favorite color."]

    </li>

    <li>

           If you wish one or more answers to lead to another conversation

           tree, then set <bold>pConvTreeResponsesNext</bold> to indicate

           subsequent conversation trees.

    </li>

    <li>

           Since the player's answers may change the NPC or other parts of

           the game, you'll probably need to write your

           own <bold>ConvTreeResponse()</bold> to handle and coding.

           For example: If the player likes red, the NPC might give them

           a red riding cloak; this needs to be handled by code.

    </li>

</ol>

 

 

<p>

    You may also wish to set the following properties and methods:

</p>

 

<ul>

    <li>

           Set <bold>pConvTreeMustAnswer</bold> to TRUE if the NPC requires

           an answer from the player and won't be distracted by attempts

           to change the subject.

    </li>

    <li>

           <bold>pConvTreeQuestionRepeat</bold> is spoken if the player

           doesn't answer the NPC's question properly and the NPC has to

           repeat the question.

    </li>

    <li>

           If you wish to customize pConvTreeQuestion or

           pConvTreeQuestionRepeat based upon the current situation,

           you can write your own <bold>ConvTreeQuestion()</bold> method.

    </li>

    <li>

           You can allow for alternate phrasings of the answers, such as

           "I like red best." by proving

           a <bold>pConvTreeAnswersParse</bold> property.

    </li>

    <li>

           Answers won't always be valid every time a conversation tree

           pops up. For example: If the NPC asks the player, "What inn are

           you staying at?", you could include answers for all the inns in

           town. However, if the player character didn't know about some of

           the inns, you might want to invalidate and hide some of the

           answers. To do this, write your

           own <bold>ConvTreeAnswerIsValid()</bold> method.

    </li>

    <li>

           <bold>pConvTreeResponseNotAnswer</bold> controls what the NPC says

           if the player tries to change the subject and avoid answering

           the question.

    </li>

    <li>

           <bold>pConvTreeResponseNotAnswerLike</bold> controls how much

           the NPC's like/trust of the PC changes when the player tries

           to change the subject.

    </li>

    <li>

           <bold>pConvTreeImportantDecision</bold> will cause all the context-menu

           options to be flagged so the player knows they're making an important decision.

    </li>

    <li>

           If you invoke the converation tree using AIConversationConvTreeAdd()

           then you may wish to write a <bold>ConvTreeIsValid()</bold> method

           so that the conversation tree won't be brought up in the conversation

           if it's no longer valid.

    </li>

    <li>

           <bold>pConvElementConversationState</bold> is used when you call

           AIConversationConvTreeAdd() to determine the conversation state after

           the conversation tree finishes.

    </li>

</ul>

 

 

 

 

<br/><section><p><bold><big>Activating a conversation tree</big></bold></p></section>

 

<p>

    There are several ways that you can activate a conversation tree:

</p>

 

<ul>

    <li>

           Call <bold>oNPC.AIConversationConvTreeSet()</bold> to start the conversation

           tree immediately.

    </li>

    <li>

           <bold>oNPC.AIConversationConvTreeAdd()</bold> will add the conversation tree

           to the NPC's list of "questions" that the NPC might choose to bring up, depending

           upon where the conversation takes it. Unless you set the priority to a very

           large value (greater than 1.0), there's no guarantee that the conversation

           tree will be run immediately.

    </li>

    <p>

           If you're providing a <bold>oNPC.AIConversationQuestionFill()</bold> method

           for your NPC, which controls what questions the NPC asks, you can

           also call AIConversationConvTreeAdd().

    </p>

    <li>

           You can have a cFactoid invoke a conversation tree by

           setting <bold>oFactoid.pFactoidConvTree</bold>.

    </li>

</ul>

 

 

 

 

<br/><section><p><bold><big>Easy conversation trees</big></bold></p></section>

 

<p>

    Often, you'll want NPCs to ask a series of questions to players as they get

    to know them. The questions will start out easy, like "Who do you want to win the

    world series?" and gradually work up to something more contentious, like

    "Do you want the elves or the orcs to win the war?"

</p>

 

<p>

    Creating this question ladder is easy, just fill in oNPC.<bold>pAIConvTrees</bold> with

    the series of questions and acceptable responses.

</p>

16 Conversation stories – cConvStory

Code that allows NPCs to tell stories to player.

 

<section><p><bold><big>Conversation stories - cConvStory</big></bold></p></section>

 

<p>

    An important part of Circumreality gameplay is for non-player characters to

    tell stories to the players. The stories not only provide knowledge

    (cKnowledge) to the player characters, but they provide clues

    about the NPC's personality. These clues help the player determine

    what needs to be done to get on the NPC's good side.

</p>

 

 

 

<br/><section><p><bold><big>Creating a story object</big></bold></p></section>

 

 

<p>

    Getting NPCs to speak stories is pretty easy, and involves the creation

    of an object for each story:

</p>

 

<ol>

    <li>

           Create an object, as normal, but base it off

           of <bold>cConvStory</bold>. For this example, call

           it oConvStoryFishingTrip.

    </li>

    <li>

           The object should <bold>not be contained</bold> in anything.

    </li>

    <li>

           Fill <bold>pConvStorySpeak</bold> with the &lt;CutScene&gt; resource

           for the story. If you just wish the NPC to speak the story, you

           can also use a &lt;SpeakScript&gt; resource, or even a string.

           If pConvStorySpeak is a list, then the story will

           be <bold>multi-part</bold>, and require the player to speak,

           "Please continue" after each part.

    </li>

    <li>

           <bold>pConvStoryAskSpeak</bold> should be filled with a unique string

           that the player can speak to the NPC and re-hear the story.

           For example: "Could you tell me about your fishing trip to the Catskills?"

    </li>

    <li>

           <bold>pConvStoryIsRumor</bold> should be set to TRUE if the story

           is actually a rumor.

    </li>

    <li>

           <bold>pConvElementConversationState</bold> is a list of lower-case

           strings that described what the story is about. For example:

           ["fishing", "catskills", "holiday"]. Every story needs this

           property since it allows the NPC to determine which stories to use

           given the conversation context.

    </li>

    <p>

           If your code access pConvElementConversationState, it should

           call <bold>ConvElementConversationState()</bold> instead, since this

           method appends "story" or "rumor" to the list based on

           pConvStoryIsRumor.

    </p>

    <li>

           You either need to <bold>sub-class your story object from cKnowledge

           as well as cConvStory</bold> or,

           set <bold>pConvStoryKnowledge</bold> to the associated knowledge

           object. This means that every time a player hears a story/rumor, they

           will be able to access it from their "What do I know?" list, as well

           as tell it to other NPCs in story/rumor swaps.

    </li>

</ol>

 

 

<p>

    You may also wish to fill in the following properties or methods:

</p>

 

<ul>

    <li>

           <bold>ConvStorySpeak()</bold> and <bold>ConvStoryNumParts()</bold> can

           be overridden so that the story changes depending upon the player.

    </li>

    <li>

           <bold>pConvStoryWontSpeak</bold> is the text that the NPC speaks

           if it isn't willing to tell the player the story just yet, perhaps

           because the NPC doesn't like the player enough.

    </li>

    <li>

           You can write your own <bold>ConvStoryWontSpeak()</bold> to decide

           when the NPC will speak the story to the player. The

           default method tests the properties: <bold>pConvStoryWontSpeakLike,

           pConvStoryWontSpeakFaction, pConvStoryWontSpeakFracture,

           and pConvStoryWontSpeakQuest</bold>.

    </li>

    <li>

           <bold>pConvStoryAskSpeakParse</bold> provides for other ways to

           ask the pConvStoryAskSpeak question.

    </li>

    <li>

           <bold>pConvStoryAskSpeakAlwaysShow</bold> causes the pConvStoryAskSpeak

           question to always be listed in the NPC's context menu, even if the

           player hasn't heard the story.

    </li>

    <li>

           <bold>pConvElementPriorty</bold> affects how badly the NPC wishes

           to tell the story. Values range from 1.0 (very badly) to 0.0 (doesn't

           wish to tell).

    </li>

    <li>

           If <bold>pConvStoryExpectPCToReciprocate</bold> then after the NPC

           finishes with the story/rumor, it will expect the player to follow

           with their own story/rumor. Players will be shown their, "What do I know?"

           list and be able to select a story to speak back.

    </li>

    <li>

           Some NPCs just like to tell stories to NPCs. If

           you set <bold>pConvStorySpeakLike</bold> then every time the NPC

           gets to tell a story/rumor to a character, it will like/trust the

           character more. Use this to create NPCs that just want a shoulder

           to cry on, or someone to brag to.

    </li>

</ul>

 

 

<br/><section><p><bold><big>Speaking a story</big></bold></p></section>

 

<p>

    There are several ways that you can have a NPC speak a story:

</p>

 

<ul>

    <li>

           Call <bold>oNPC.AIConversationConvStorySet()</bold> to speak the story immediately.

    </li>

    <li>

           Call <bold>oNPC.AIConversationConvStoryAdd()</bold> will add the story

           to the NPC's list of "questions" that the NPC might choose to bring up, depending

           upon where the conversation takes it. Unless you set the priority to a very

           large value (greater than 1.0), there's no guarantee that the story

           will be spoken immediately.

    </li>

    <p>

           If you're providing a <bold>oNPC.AIConversationQuestionFill()</bold> method

           for your NPC, which controls what questions the NPC asks, you can

           also call AIConversationConvTreeAdd(). You probably won't need to; see below.

    </p>

    <li>

           For each cCharacter and cFaction, you can

           set <bold>pAIConvStories</bold> to a list of stories that the character (or

           faction) knows. Characters will automatically know stories assigned to

           their factions.

    </li>

    <p>

           Using pAIConvStories will automatically add stories to the list of "questions"

           when oNPC.AIConversationQuestionFill() is called, as well as provide

           context menu options for the stories.

    </p>

    <li>

           A call to <bold>ConvStorySelect()</bold> will select a story that seems

           most appropriate to the NPC, given the current conversation state and

           stories that the NPC has already spoken to the player.

    </li>

</ul>

 

 

 

 

<br/><section><p><bold><big>Properties associates with NPCs</big></bold></p></section>

 

<p>

    You may wish to set the following properites for every NPC:

</p>

 

<ul>

    <li>

           <bold>pAIConvStories</bold>, as previously stated, controls the stories

           that the NPC knows. (It also knows the cFaction.pAIConvStories of any

           factions to which it belongs.)

    </li>

    <li>

           <bold>pAIConvStoryPreferred</bold> is a list of the topics (aka: a conversation

           state) in stories that the NPC prefers to speak, as well as hear.

           For example: If a NPC's pAIConvStoryPreferred is ["story", "fishing", "cars",

           "holiday"], then the NPC will prefer to tell stories about fishing, cars,

           and/or holidays. Likewise, they will prefer to hear such stories.

           This property is very important because it <bold>defines much of

           the NPC's personality.</bold>

    </li>

    <p>

           A more flexible alternative to pAIConvStoryPreferred, it to

           override the method, <bold>AIConvStoryPreferred()</bold>. The default

           behavior just refers to pAIConversationStoryPreferred. However, if

           you write your own code, you can specify that all dwarves like to hear

           about "mining" and "gems", or that NPC's who are sad wish to hear "sad" stories.

    </p>

    <li>

           <bold>pAIConvStoryStickToPreferred</bold> controls how much the NPC

           sticks to its pAIConvStoryPreferred list during conversations. If this

           is 1.0, the NPC will keep returning to fishing, cars, and holidays. If

           this is 0.0, the NPC will easily follow the player's lead.

    </li>

    <li>

           By default, if the NPC knows any stories or rumors, it will include

           the context menu of "Do you know any good rumors/stories?" to save

           players some typing. However, this context menu reveals to the player

           the fact that the NPC knows some stories/rumors. If you wish

           to hide the menu, then

           set <bold>pAIConvStoryHideRumorMenu</bold> to TRUE.

    </li>

</ul>

 

 

<br/><section><p><bold><big>Conversation states</big></bold></p></section>

 

<p>

    As stated, the pConvElementConversationState property is very important for

    stories, since it controls when a NPC will speak a story, and what

    the NPC thinks about stories told to it by the player. The property

    is a list of lower-case strings that describe the story, such as ["story",

    "fishing", "holiday", "catskills"].

</p>

 

<p>

    The NPC's AI determines which stories follow one another best by

    calling <bold>ConvStoryConversationIntersect()</bold>, comparing

    the existing conversation state to the story's conversation state.

</p>

 

<p>

    Some converation state topics are opposites of one another. For example:

    "rumor" is the opposite of story, and "work" is the opposite of "holiday".

    NPCs take extra precaution to not speak a follow-on story with opposites

    to the current conversation state. For example: A story about a holiday

    followed by a story about work (holiday's opposite) is a social mistake

    that's to be avoided. Players need to work within the "avoid opposites"

    framework too.

</p>

 

<p>

    To determine a conversation state's

    opposites, <bold>ConversationStateOpposites()</bold> is called.

</p>

17 Conversation messages

Easy way for NPCs to relay messages to players.

 

<section><p><bold><big>Conversation messages</big></bold></p></section>

 

<p>

    Often times, you'll want NPCs to relay messages to PCs, such as

    "Your mother called me and wants you to go home and eat dinner now,"

    or "[Other PC] has been spreading bad rumors about you."

</p>

 

<p>

    The hard way to provide this functionality is to write your

    own AIConversationQuestionFill() that calls AIConversationQuestionAdd().

</p>

 

<p>

    The easy way is to:

</p>

 

<ol>

    <li>

           Call <bold>AIConversationMessageAdd()</bold> when the NPC learns of

           a message that it wants to tell the player.

    </li>

    <li>

           That's it.

    </li>

</ol>

 

<p>

    If you want to remove a message from a NPC,

    then call <bold>AIConversationMessageRemove()</bold>. For example:

    When the player returns home from, then all the mothers in the neighborhood

    that had been called with AIConversationMessageAdd() should

    receive an AIConversationMessageRemove() call with the same

    message string.

</p>

18 Commands, statements, and questions

How to easily allow players to give NPC commands, ask them questions, or state facts to them.

 

<section><p><bold><big>Commands, statements, and questions</big></bold></p></section>

 

<p>

    There is yet another way to make NPCs more conversive; this way is

    designed so that you'll find it easy to create commands (that players can

    give to the NPCs), questions (that players can ask), and

    statements (that players can speak to NPCs). In most cases, you may

    find this easier than using factoids (cFactoid), conversation

    trees (cConvTree), or conversation stories (cConvStory).

</p>

 

 

<br/><section><p><bold><big>The easiest way</big></bold></p></section>

 

<p>

    The easiest way for a NPC to answer questions is to:

</p>

 

<ol>

    <li>

           Fill in <bold>pAIQuestionsAndAnswers</bold> with the questions and

           answers that the NPC knows.

    </li>

    <p>

           Optionally, write your own <bold>AIQuestionsAndAnswers()</bold> method.

    </p>

</ol>

 

 

<p>

    Unfortuntely, this method is somewhat limit, so you'll

    need something more flexible.

</p>

 

<br/><section><p><bold><big>A more flexible technique</big></bold></p></section>

 

<p>

    To get a NPC to respond to a custom command, answer a specific question, or

    understand a statement:

</p>

 

<ol>

    <li>

           <bold>Invent a lower-case alphabetic-only string</bold> that described

           the phrase, such as "singsong" if you want players to ask NPCs

           to sing a song.

    </li>

    <li>

           Add a <bold>prefix</bold> to the string. Use "cmd" if it's a command,

           "quest" for a question, or "state" for a statement. For the singing

           example, this would create a name of "cmdsingsong".

    </li>

    <li>

           Add <bold>AIListenForEnum()</bold> to the NPC. You'll need to

           write this code so this appends the appropriate phrases (commands, questions,

           or statements) to the list. For the singing example, this might

           append, ["cmdsingsong", "Sing a song", "sing [a] song [to %1]"].

           "cmdsingsong" is the name. "Sing a song" will appear on the NPC's context

           menu so that players know they can ask the NPC to sing. "sing [a] song [to %1]"

           is an additional parse that instructs the NPC to sing their song to

           a specific character.

    </li>

    <p>

           If you don't include the second element in the list, "Sing a song", then

           the command <bold>won't</bold> appear on the NPC's context menu, requiring

           users to type it in.

    </p>

    <li>

           You'll need to add <bold>AIListenForIsValid()</bold> to validate

           that the pameters for the %1 wildcard is correct. In this case,

           if the (PhraseID === "cmdsingsong") then the method should verify that

           there are no parameters, in the case of "Sing a song", or that there is

           one parameter and it is another character, for "Sing a song to %1".

    </li>

    <li>

           Finally, write a <bold>AIListenForAct()</bold> to actually

           have the NPC sing a song.

    </li>

    <li>

           If you only want the NPC to <bold>speak a phrase in reply</bold>, such as to the

           question, "What is the meaning of life?", then you can get away with

           only an AIListenForEnum() that fills the list in with ["questmeaningoflife",

           "What is the meaning of life?", "[*4] meaning [of] life [*5]", "The meaning

           of life is 42."]. The fourth parameter will be spoken if the question

           is asked. The third parameter, "[*4] meaning [of] life [*5]", accepts

           any sentence as long as it has "meaning of life" or "meaning life" in it.

           Using this approach you won't need an AIListenForIsValid() or

           AIListenForAct().

    </li>

</ol>

 

 

<br/><section><p><bold><big>Advanced tips</big></bold></p></section>

 

<ul>

    <li>

           You can also add AIListenForEnum(), AIListenForIsValid(),

           and AIListenForAct() to the NPC's <bold>cFaction</bold>, allowing

           all members of the faction to respond to the phrase.

    </li>

    <li>

           Alternatively, adding AIListenForEnum(), AIListenForIsValid(),

           and AIListenForAct() to <bold>different NPC classes</bold>, such as cNPCSinger

           or cNPCPhilisopher,

           will enable all members of the class to react to the command, question,

           or statement. The methods are all layerable, so a NPC derived

           from cNPCSinger <bold>and</bold> cNPCPhilosopher will be able to

           both sing and answer the meaning of life.

    </li>

</ul>

 

 

<br/><section><p><bold><big>Give/offer (default command)</big></bold></p></section>

 

<p>

    If a player gives an object to a NPC, this is translated into a "cmdoffer"

    ParseID that's passed into AIListenForIsValid() and AIListenForAct().

    Only one parameter is used, the object that's being given.

</p>

 

<p>

    The default implimentation checks to see the real value of the gift, along with

    the NPC's personal valuation (since some NPCs like specific types of objects,

    such as chocolates or collectables). If the real valuation is too high for

    the NPC, or the player have given the NPC too many gifts recently, then the

    NPC will decline the gift. Otherwise, the gift will be accepted, and the

    NPC's opinion of the player will be improved based upon the NPC's valuation

    of the gift (not the real value).

</p>

 

<p>

    Some properties that you might wish to modify are:

</p>

 

<ul>

    <li>

           <bold>pAIOfferCanBribe</bold> - If this is TRUE then the player can give

           money to the NPC without offending the NPC. By default, this is FALSE.

    </li>

    <li>

           <bold>pAIOfferEffectiveness</bold> - The amount that a NPC's like/trust

           go up when it's given a gift is based upon the NPC's personal valuation of

           the gift combined with the values in pAIOfferEffectiveness. Higher numbers

           will make the NPC more susceptible to gift-giving and bribery.

    </li>

    <li>

           <bold>pAIOfferForget</bold> - See below for details, but this controls

           how frequently players are able to give NPCs gifts.

    </li>

    <li>

           <bold>pAIOfferMaxAmount</bold> - This is the maximum (real) value of gifts

           that the player can give the NPC per "week". (pAIOfferForget controls how

           long a "week" is.) The value is tempered by how much the NPC likes the

           PC; with smaller amounts being used by NPCs that like the player less.

           If the player tries to give a gift that's more expensive

           than this then the NPC will reject the gift.

    </li>

</ul>

 

 

<p>

    Because this behaviour is customized so frequently, there's a special

    method that you may wish to modify, AIListenForCmdOffer().

</p>

 

 

<br/><section><p><bold><big>Show (default command)</big></bold></p></section>

 

<p>

    If a player shows an object to a NPC, this is translated into a "cmdshow"

    ParseID that's passed into AIListenForIsValid() and AIListenForAct().

    Only one parameter is used, the object that's being given.

</p>

 

<p>

    The default implimentation checks to see

    the NPC's personal valuation of the object (since some NPCs like specific types of objects,

    such as chocolates or collectables).

    The NPC then responds based on its personal valuation.

</p>

 

<p>

    Some properties that you might wish to modify are:

</p>

 

<ul>

    <li>

           <bold>pAIOfferCanBribe</bold> - NPCs that can be bribed will find

           money more interesting that those that can't be bribed.

    </li>

    <li>

           <bold>pAIOfferEffectiveness</bold> - This is used for "cmdoffer" (above),

           and to determine how interested the NPC is in the object.

    </li>

</ul>

 

 

<p>

    Because this behaviour is customized so frequently, there's a special

    method that you may wish to modify, AIListenForCmdShow().

</p>

 

 

<br/><section><p><bold><big>Can I have your OBJECT? (default command/question)</big></bold></p></section>

 

<p>

    Players can ask NPCs to give them objects that the NPCs are carrying, by typing,

    "Can I have your OBJECT".

</p>

 

<p>

    To specify what objects the NPCs are willing to give:

</p>

 

<ol>

    <li>

           Set <bold>pAIGivable</bold> to the list of objects that the NPC will give. To ensure

           that the NPC will have the objects for all players that come through a multiplayer

           game, objects in pAIGivable will (by default) be automatically created in the NPC's

           possession.

    </li>

</ol>

 

 

<br/><section><p><bold><big>Default questions</big></bold></p></section>

 

<p>

    NPCs understand dozens of default questions, such as "What race are you?",

    "What guilds are you a member of?", etc.

</p>

 

<p>

    You can fine-tune how readily the NPC answers these questions by

    setting:

</p>

 

<ul>

    <li>

           <bold>pAIInfoReveal</bold> controls how tight-lipped the NPC is about

           information. If you don't set this, the value will be randomly generated.

    </li>

    <li>

           <bold>AIInfoRevealValue()</bold> is a layered method to see how tight lipped the AI

           is about a specific type of information, such as the languages that it

           speaks or its hobbies. This is called by AIInfoReveal() (see below) to

           see whether a NPC will reveal specific information.

    </li>

    <li>

           While you won't modify this, <bold>AIInfoReveal()</bold> is

           a method to see how tight lipped the AI

           is about a specific type of information, such as the languages that it

           speaks or its hobbies. Call this to see if the AI would be willing

           to reveal some information to the player.

    </li>

</ul>

 

<p>

    You may also wish to override some of the default responses:

</p>

 

<p>

    You can add a layer to <bold>AIListenForAct()</bold>, as described above.

    This will cause, for example, a specific question to a specific NPC

    to be trapped. If you ask Fred, "Is Mike a member of any guilds?", Fred

    will have a unique answer.

</p>

 

<p>

    If you wish to have <bold>all NPCs</bold> that know Mike to provide

    a custom response to, "Is Mike a member of any guilds?", then...

</p>

 

<ul>

    <li>

           Write you own <bold>AIInfoSpeak()</bold> associated with the object about

           which the question is being asked. For this example, you would

           write oMike.AIInfoSpeak().

    </li>

    <li>

           And/or, set <bold>oMike.pAIInfoSpeakFuturePlansNPC</bold> to provide a default

           response to the question, "What are Mike's future plans?"

    </li>

    <li>

           And/or, set <bold>oMike.pAIInfoSpeakHowDoingNPC</bold> to provide a default

           response to the question, "How is Mike doing?" or "How are you doing?"

    </li>

    <li>

           And/or, set <bold>oMike.pAIInfoSpeakKnowAboutNPC</bold> to provide a default

           response to the question, "What do you know about Mike?"

    </li>

    <li>

           And/or, set <bold>oMike.pAIInfoSpeakOccupationNPC</bold> to provide a default

           response to the question, "What is Mike's occupation?" or "What is your occupation?"

    </li>

    <li>

           And/or, set <bold>oMike.pAIInfoSpeakWhereFromNPC</bold> to provide a default

           response to the question, "Where is Mike from?"

    </li>

    <li>

           And/or, set <bold>oMike.pAIInfoSpeakWhereLiveNPC</bold> to provide a default

           response to the question, "Where does Mike live?" or "Where do you live?"

    </li>

    <li>

           And/or, set <bold>oMike.pAIInfoSpeakWhyHereNPC</bold> to provide a default

           response to the question, "Where is Mike here?"

    </li>

    <p>

           The default location for where the character lives is

           generated from <bold>pAIScheduleLocationLive</bold>.

    </p>

    <li>

           And/or, set <bold>oMike.pAIInfoSpeakWhereWorkNPC</bold> do provide a default

           response to the question, "Where does Mike work?" or "Where do you work?"

    </li>

    <p>

           The default location for where the character works is

           generated from <bold>pAIScheduleLocationWork</bold>.

    </p>

    <li>

           Fill <bold>pAIInfoSpeakAdvice</bold> with the NPC's answer to "Do you

           have any advice for me?"

    </li>

    <li>

           Fill <bold>pAIInfoSpeakHappening</bold> with the NPC's answer to "What's

           going on here?"

    </li>

</ul>

 

 

19 Chatter-bot functionality

How to easily incorporate chatter-bot functionality into your NPCs.

 

<section><p><bold><big>Chatterbot functionality</big></bold></p></section>

 

<p>

    So far, all the NPC conversation tools that you've been shown are designed

    to (a) create procedural responses to questions, like "Where is fred?", and

    (b) create NPC-specific responses, such as "Why are you here?"

</p>

 

<p>

    Sometimes, though, you may just need basic chatter-bot functionality.

    The player types in a statement or question, and the NPC responds with

    a statement (or a statement that ends in a question mark). In general,

    the same response is provided by all NPCs, or at least, all NPCs

    of a specific class (such as all elves).

</p>

 

<p>

    To create a chatterbot:

</p>

 

<ol>

    <li>

           For a class of NPC, such as cElf, <bold>write a cElf.AIConvChatter()</bold> method.

           This will fill in a list with [ResourceName, Resource]. In the case

           of elves, this might be ["elf", rNLPRuleSetElfChatter].

    </li>

    <p>

           Alternatively, you could fill in <bold>pAIConvChatter</bold>, but this isn't

           recommended for classes of NPCs, because one class might overwrite another

           class's pAIConvChatter if a NPC based on two classes, each with their own

           chatter-bot rules.

    </p>

    <li>

           <bold>Create a &lt;NLPRuleSet&gt; resource</bold> named rNLPRuleSetElfChatter (or

           whatever you called it above.

    </li>

    <li>

           In the rule set, <bold>create rules</bold> that convert sentences the player may speak

           into parsed sentences of the format, "`speak WHATTOSPEAK".

    </li>

    <p>

           For example: If you want players to ask, "What is the meaning of life?",

           then create a rule that converts "what is the meaning of life" to

           "`speak The meaning of life? 42, of course.".

    </p>

</ol>

 

<p>

    That's it. From now on, players will be able to ask all elves, "What is the

    meaning of life?", and all elves will reply with the exact same answer,

    "The meaning of life? 42, of course."

</p>

 

<p>

    This simple method has several limitations: (a) You can't assign transplanted

    prosody to the response, (b) You can't assign a &lt;SpeakScript&gt; to the response.

    And (c) you can't customize it according to the NPC, PC, and situation.

</p>

 

<p>

    A more sophisticated approach is:

</p>

 

<ol>

    <li>

           <bold>Create a rule</bold> that converts the english sentence to a parsed

           sentence with the form, "`chatter `RESOURCENAME `SENTENCENAME". RESOURCENAME

           is the name you used for the resource, above, "elfchatter". SENTENCENAME

           uniquely identified the response for "What is the meaning of life?"

    </li>

    <p>

           For example: Your rule set would convert "what is the meaning of life" to

           "`chatter `elf `whatmeaningoflife".

    </p>

    <li>

           <bold>Write a cElf.AIConvChatterAct()</bold> method. In the method, first

           test that (ResourceName == "elf"), returning if it isn't. Then, test

           that (SentenceName == "whatmeaningoflife"), returning "The meaning of life?

           42, of course." if there's a match.

    </li>

    <li>

           If the method <bold>returns the resource</bold> sAIConvChatterWhatMeaningOfLife,

           then you can record transplanted prosody.

    </li>

    <li>

           Or, the method <bold>could return a &lt;SpeakScript&gt; resource.</bold>

    </li>

    <li>

           Or, the method <bold>could run any code it wishes</bold>, and have the elf break into

           song, and then return TRUE to indicate that the AIConvChatterAct() method

           handled all the speaking.

    </li>

</ol>

20 Merchants – cMerchant

The cMerchant class makes it easy to add a merchant to your world.

 

<section><p><bold><big>Merchants - cMerchant</big></bold></p></section>

 

<p>

    Merchants are a common fixture in most worlds.

    The <bold>cMerchant</bold> class was created to make implimenting

    merchants easy.

</p>

 

<p>

    Merchants can perform the following functionality:

</p>

 

<ul>

    <li>

           <bold>Sell</bold> equipment to player characters.

    </li>

    <li>

           <bold>Buy</bold> equipment from player characters.

    </li>

    <li>

           <bold>Change</bold> currency from one form to another (gold to copper).

    </li>

    <li>

           <bold>Repair</bold> equipment.

    </li>

    <li>

           <bold>Identify</bold> equipment.

    </li>

    <li>

           <bold>Teach</bold> player characters skills.

    </li>

    <li>

           <bold>Heal</bold> charaters.

    </li>

    <li>

           <bold>Other</bold> actions; whatever the author thinks up.

    </li>

</ul>

 

 

<p>

    To create a merchant:

</p>

 

<ol>

    <li>

           <bold>Create</bold> your NPC as usual.

    </li>

    <li>

           Add an extra "Super-class", <bold>cMerchant</bold>, on top

           of (and with higher priority than) the NPC's basic class.

    </li>

    <li>

           <bold>Specific properties</bold> are required depending upon exactly what

           the NPC will sell. See below.

    </li>

</ol>

 

 

 

 

<br/><section><p><bold><big>Selling equipment</big></bold></p></section>

 

 

<p>

    If you wish your merchant to sell equipment:

</p>

 

<ol>

    <li>

           Fill in <bold>pMerchantForSale</bold> with a list of items that

           the merchant is selling. For each item, an object class, the

           number of items in stock, the restock rate, and the maximum

           stock are stored. You can also customize the price for an item,

           making it more or less expensive for a particular merchant.

    </li>

    <p>

           You can leave this blank if you fill in pMerchantForSaleInit; see

           below.

    </p>

    <li>

           Since many merchants sell the same basic goods, such as bandages,

           you can fill in <bold>pMerchantForSaleInit</bold> with a series

           of callback functions that indicate "classes" of objects that

           are sold. The fantasy library containes some

           default callback functions, <bold>MerchantForSaleInitArmor(),

           MerchantForSaleInitWeapons(), MerchantForSaleInitFirstAid(),

           MerchantForSaleInitLighting(),

           and MerchantForSaleInitContainers()</bold> which include default

           armor, weapons, firt-aid equipment, lighting, and containers.

           For each of these callbacks, you can scale the number of goods

           a merchant stocks, how quickly the stock arrives, and how much

           the merchant charges for the stock.

    </li>

    <p>

           You can <bold>write your own callbacks</bold> if you wish.

    </p>

</ol>

 

<p>

    You might wish to change some other properties of the merchant:

</p>

 

<ul>

    <li>

           <bold>pAIValueObject</bold> will cause the merchant to value some

           items more or less than others. This is another way of increasing

           or decreasing the amount the merchant will charge.

    </li>

    <li>

           <bold>gMerchantCurrencyError</bold> affects the amount that the

           merchant will round a price to. The default is 2 (percent).

           See <bold>CurrencyIAToCoin()</bold>

    </li>

    <li>

           <bold>gMerchantCurrencyMax</bold> controls how many currencies the

           merchant will include in a price. The default is 2, allowing

           for prices like "2 gp, 1 sp", not "2 gp, 1 sp, 3 cp".

           See <bold>CurrencyIAToCoin()</bold>

    </li>

    <li>

           <bold>pMerchantLikeScale</bold> affects how much the merchant will

           adjust his price based on how much the merchant likes (or dislikes) the

           character he is selling to.

    </li>

    <li>

           <bold>pMerchantLikeThreshhold</bold> will cause merchants to refuse

           to sell (or do any transaction with) characters the merchant doesn't

           like.

    </li>

</ul>

 

 

<br/><section><p><bold><big>Buying equipment</big></bold></p></section>

 

 

<p>

    If you wish the merchant to buy equipment from players:

</p>

 

<ol>

    <li>

           Set <bold>pMerchantBuyBack</bold> to TRUE so the merchant is allowed

           to purchase used equipment.

    </li>

</ol>

 

<p>

    You may also wish to set the following values:

</p>

 

<ul>

    <li>

           <bold>pMerchantBuyBackNotNormallyStocked</bold> affects the price

           the merchant is willing to pay for items that the merchant doesn't

           normally stocked. If not specified, the number is randomly

           generated.

    </li>

    <li>

           <bold>pMerchantCash</bold> specified how much money the merchant

           has, and ultimately how expensive of items the merchant can buy.

    </li>

    <li>

           <bold>gMerchantBuyBackScale</bold> controls the price

           that <bold>all</bold> merchants are willing to pay for used equipment.

           This global will help you control your world's economy, affecting

           how much money is introduced into the world by merchants.

    </li>

    <li>

           When the merchant purchases an item that isn't normally

           stocked, <bold>gMerchantBuyBackSellDown</bold> determines how long

           the item will remain on the merchant's list before being "sold off"

           and removed from the list.

    </li>

    <li>

           <bold>pMerchantForSale, pMerchantLikeScale, pAIValueObject,

           and pMerchantLikeThreshhold</bold> will also affect the merchant's

           purchasing of used equipment.

    </li>

</ul>

 

 

 

<br/><section><p><bold><big>Making change</big></bold></p></section>

 

 

<p>

    By default, merchants are willing to exchange coins for larger

    or smaller denominations with players. If you wish to

    disable this:

</p>

 

<ol>

    <li>

           Set <bold>pMerchantChange</bold> to FALSE to prevent a merchant from

           making change.

    </li>

</ol>

 

 

<p>

    You may also wish to set the following values:

</p>

 

<ul>

    <li>

           <bold>pMerchantLikeThreshhold</bold> will affect whether the merchant

           will make change for the player.

    </li>

</ul>

 

 

 

 

 

<br/><section><p><bold><big>Repairing objects</big></bold></p></section>

 

 

<p>

    To allow a merchant to repair objects:

</p>

 

<ol>

    <li>

           Set <bold>pMerchantRepair</bold> to TRUE.

    </li>

    <li>

           Modify the NPC's <bold>pSkills</bold> property so it includes the

           appropriate repair skills. For example: A blacksmith would

           have oSkillBlacksmith, while a leather worker would have oSkillLeatherWorking.

    </li>

    <li>

           Make sure all the <bold>tools</bold> that the merchant needs to

           repair are either held by the NPC or located in the same room. Merchants

           can't repair without tools. They <bold>can</bold> get buy without

           materials though.

    </li>

</ol>

 

 

<p>

    You may also wish to set the following values:

</p>

 

<ul>

    <li>

           <bold>pMerchantLikeThreshhold</bold> will affect whether the merchant

           will make change for the player.

    </li>

    <li>

           <bold>pMerchantCash</bold> affects how much cash the merchant has

           on hand, and (more importantly) how much patronage will affect

           how much the merchant likes the player's character. Spending lots

           of money with a merchant make the merchant like the player character better.

    </li>

</ul>

 

 

 

<br/><section><p><bold><big>Identifying objects</big></bold></p></section>

 

 

<p>

    To allow a merchant to identify objects:

</p>

 

<ol>

    <li>

           Set <bold>pMerchantIdentify</bold> to TRUE.

    </li>

    <li>

           Modify the NPC's <bold>pSkills</bold> property so it includes the

           appropriate identification skills. For example: A botonist might

           have oSkillBotony, while a magical scholar might have oSkillMagicHistory.

    </li>

</ol>

 

 

<p>

    You may also wish to set the following values:

</p>

 

<ul>

    <li>

           <bold>pMerchantLikeThreshhold</bold> will affect whether the merchant

           will make change for the player.

    </li>

    <li>

           <bold>pMerchantCash</bold> affects how much cash the merchant has

           on hand, and (more importantly) how much patronage will affect

           how much the merchant likes the player's character. Spending lots

           of money with a merchant make the merchant like the player character better.

    </li>

</ul>

 

 

 

 

<br/><section><p><bold><big>Bankers</big></bold></p></section>

 

 

<p>

    To allow a merchant to be a banker:

</p>

 

<ol>

    <li>

           Set <bold>pMerchantBanker</bold> to 1. If you use a higher value then

           the merchant will charge more for deposits than normal. Lower values (more

           than 0) will resulting in cheaper banking.

    </li>

</ol>

 

 

<p>

    You may also wish to set the following values:

</p>

 

<ul>

    <li>

           <bold>gBankerPricePerVolume</bold> is the "average" that a banker

           charges to store 1 liter of object volume.

    </li>

    <li>

           <bold>gBankerMaxDeposit</bold> is the maximum number of items a

           character can store in their bank accounts. This ensures that characters

           don't hoard too much equipment.

    </li>

    <li>

           <bold>pMerchantLikeThreshhold</bold> will affect whether the merchant

           will take deposits from the character..

    </li>

    <li>

           <bold>pMerchantCash</bold> affects how much cash the merchant has

           on hand, and (more importantly) how much patronage will affect

           how much the merchant likes the player's character. Spending lots

           of money with a merchant make the merchant like the player character more.

    </li>

</ul>

 

 

 

<br/><section><p><bold><big>Teach skills</big></bold></p></section>

 

 

<p>

    To allow a merchant to teach skills to a player:

</p>

 

<ol>

    <li>

           Set <bold>pMerchantSkills</bold> to a list of skills that the merchant

           teaches, along with the cost to learn the skill.

    </li>

</ol>

 

 

<p>

    You may also wish to set the following values:

</p>

 

<ul>

    <li>

           <bold>pMerchantLikeThreshhold</bold> will affect whether the merchant

           will teach the character.

    </li>

    <li>

           <bold>pMerchantCash</bold> affects how much cash the merchant has

           on hand, and (more importantly) how much patronage will affect

           how much the merchant likes the player's character. Spending lots

           of money with a merchant make the merchant like the player character more.

    </li>

</ul>

 

 

 

 

<br/><section><p><bold><big>Healer</big></bold></p></section>

 

 

<p>

    To allow a merchant to heal the player's wounds:

</p>

 

<ol>

    <li>

           Use the <bold>cMerchantHealer</bold> class instead of the

           cMerchant class.

    </li>

    <li>

           You may want to adjust <bold>pMerchantHealer</bold> to control

           how expensive/cheap the merchant is.

    </li>

    <li>

           <bold>pSkills</bold> can be changed so the merchant has

           a higher or lower oSkillFirstAid, which affects what kind of

           healing is possible as well as the speed at which the healing

           occurs.

    </li>

</ol>

 

 

<p>

    You may also wish to set the following values:

</p>

 

<ul>

    <li>

           <bold>gHealerPriceBandage, gHealerPriceSplint,

           and gHealerPriceRegrow</bold> affect the typical cost of

           getting an injury healed.

    </li>

    <li>

           <bold>pMerchantLikeThreshhold</bold> will affect whether the merchant

           will heal the character.

    </li>

    <li>

           <bold>pMerchantCash</bold> affects how much cash the merchant has

           on hand, and (more importantly) how much patronage will affect

           how much the merchant likes the player's character. Spending lots

           of money with a merchant make the merchant like the player character more.

    </li>

</ul>

 

 

 

 

 

<br/><section><p><bold><big>Miscellanrous services</big></bold></p></section>

 

 

<p>

    Healers are really based on the merchant's "Miscellaneous" services

    option. If you wanted to create a barber, for example, you'd write

    some code to allow for miscellaneous services:

</p>

 

<ol>

    <li>

           Write a <bold>MerchantMiscEnum()</bold> method for the merchant. This

           method enumerates all the miscellaneous services the merchant offers.

    </li>

    <li>

           Write a <bold>MerchantMiscDo()</bold> method, causing the merchant

           to act on a purchased service.

    </li>

    <li>

           Optionally, set <bold>pMerchantMiscCommand</bold> to the phrase

           displayed in the merchant's context menu. The default is,

           "Do you provide any services?". You could change this to,

           "I want my hair cut."

    </li>

    <p>

           For this to work, you must include the parser rules in the merchant

           for "I want my hair cut" to either "`merchantforsale `misc" or

           "`merchantforsale `hair". (See below.)

    </p>

    <li>

           If you use the "`merchantforsale `hair" option (or another custom

           tag), you'll need

           to set <bold>pMerchantMiscCommandTag</bold> to "`hair".

    </li>

</ol>

 

 

<p>

    You may also wish to set the following values:

</p>

 

<ul>

    <li>

           <bold>pMerchantLikeThreshhold</bold> will affect whether the merchant

           will provide a service for the character.

    </li>

    <li>

           <bold>pMerchantCash</bold> affects how much cash the merchant has

           on hand, and (more importantly) how much patronage will affect

           how much the merchant likes the player's character. Spending lots

           of money with a merchant make the merchant like the player character more.

    </li>

</ul>

 

 

 

<br/><section><p><bold><big>Miscellanrous notes</big></bold></p></section>

 

<p>

    You may also wish to modify the following:

</p>

 

<ul>

    <li>

           <bold>MerchantIsOpen()</bold> can be overridden to see if the

           merchant is open for business.

    </li>

</ul>

 

 

 

21 Relationships – cRelationship

Creating relationships between your NPCs.

 

<section><p><bold><big>Relationships - cRelationship</big></bold></p></section>

 

<p>

    In Circumreality, NPCs can have <bold>relationships</bold> with other NPCs. These might

    include being a spouse, child, coworker, friend, etc. The relationshps

    a NPC has affects how much one NPC knows about another, as well as how

    much the NPC is willing to say about the other NPC.

</p>

 

 

 

<br/><section><p><bold><big>cRelationship object</big></bold></p></section>

 

 

<p>

    Before specifying what relationships a NPC has, you must first specify

    the types of relationships: spouses, parents, children, enemies, friends, etc.

</p>

 

<p>

    The following relationships are already built into Circumreality, and handled by

    the listed object:

</p>

 

<small><p align=center><table width=80%>

    <tr>

           <td><bold>Major relationship</bold></td>

           <td><bold>Minor relationship</bold></td>

           <td><bold>Object</bold></td>

           <td><bold>Inverse</bold></td>

    </tr>

    <tr>

           <td>"auntuncle"</td>

           <td><italic>None</italic></td>

           <td>oRelationshipAuntUncle</td>

           <td>"niecenephew"</td>

    </tr>

    <tr>

           <td>"child"</td>

           <td><italic>None</italic></td>

           <td>oRelationshipChild</td>

           <td>"parent"</td>

    </tr>

    <tr>

           <td>"coworker"</td>

           <td><italic>None</italic></td>

           <td>oRelationshipCoWorker</td>

           <td>"coworker"</td>

    </tr>

    <tr>

           <td>"employee"</td>

           <td><italic>None</italic></td>

           <td>oRelationshipEmployee</td>

           <td>"employer"</td>

    </tr>

    <tr>

           <td>"employer"</td>

           <td><italic>None</italic></td>

           <td>oRelationshipEmployer</td>

           <td>"employee"</td>

    </tr>

    <tr>

           <td>"enemy"</td>

           <td><italic>None</italic></td>

           <td>oRelationshipEnemy</td>

           <td>"enemy"</td>

    </tr>

    <tr>

           <td>"friend"</td>

           <td><italic>None</italic></td>

           <td>oRelationshipFriend</td>

           <td>"friend"</td>

    </tr>

    <tr>

           <td>"friend"</td>

           <td>"good"</td>

           <td>oRelationshipFriendGood</td>

           <td>"friend"</td>

    </tr>

    <tr>

           <td>"girlboyfriend"</td>

           <td><italic>None</italic></td>

           <td>oRelationshipGirlBoyFriend</td>

           <td>"girlboyfriend"</td>

    </tr>

    <tr>

           <td>"girlboyfriend"</td>

           <td>"hidden"</td>

           <td>oRelationshipGirlBoyFriendHidden</td>

           <td>"girlboyfriend", "hidden"</td>

    </tr>

    <tr>

           <td>"grandchild"</td>

           <td><italic>None</italic></td>

           <td>oRelationshipGrandChild</td>

           <td>"grandparent"</td>

    </tr>

    <tr>

           <td>"grandparent"</td>

           <td><italic>None</italic></td>

           <td>oRelationshipGrandParent</td>

           <td>"grandchild"</td>

    </tr>

    <tr>

           <td>"niecenephew"</td>

           <td><italic>None</italic></td>

           <td>oRelationshipNieceNephew</td>

           <td>"auntuncle"</td>

    </tr>

    <tr>

           <td>"parent"</td>

           <td><italic>None</italic></td>

           <td>oRelationshipParent</td>

           <td>"child"</td>

    </tr>

    <tr>

           <td>"rival"</td>

           <td><italic>None</italic></td>

           <td>oRelationshipRival</td>

           <td>"rival"</td>

    </tr>

    <tr>

           <td>"sibling"</td>

           <td><italic>None</italic></td>

           <td>oRelationshipSibling</td>

           <td>"sibline"</td>

    </tr>

    <tr>

           <td>"spouse"</td>

           <td><italic>None</italic></td>

           <td>oRelationshipSpouse</td>

           <td>"spouse"</td>

    </tr>

</table></p></small>

 

 

<br/><section><p><bold><big>cRelationship properties</big></bold></p></section>

 

<p>

    Every relationship object (associated with a relationship) has a list

    of properties that describe the "typical" relationship:

</p>

 

<ul>

    <li>

           <bold>pNLPNounName</bold> and <bold>pNLPParseName</bold> are used to

           name the relationship, such as "grandmother".

    </li>

    <li>

           <bold>pRelationshipInfluence</bold> simulates the NPC telling its

           relations about what a good/bad guy the player is. If this is a large number

           then any like/trust impression that the player character makes on the

           NPC also has a large impact on the NPC's relations. Thus, a "spouse" relationship

           would have a much larger impact than a "coworker" relationship. This

           number can be negative for an "enemy" relationship where making friends with

           one NPC causes the NPC's enemies to become unfriendly towards the player.

    </li>

    <li>

           <bold>pRelationshipInverse</bold> indicates the opposite relationship.

           Thus, if NPC A is a "employer" to NPC B, then NPC B must be an

           "employee" to NPC A.

    </li>

    <li>

           <bold>pRelationshipLike</bold> indicates how much the NPC likes/trusts his

           relations. "friend" might have a [1, 1] indicating theat if NPC A is a friend

           of NPC B then, in general, NPC A's like/trust of NPC B is +1.0 higher than

           one would normally expect. Conversely, "enemy" might be [-6, -6], indicating

           a default like/trust of 6-points below.

    </li>

    <li>

           <bold>pRelationshipString"</bold> is the lower-case string for the relationship,

           such as "spouse".

    </li>

    <li>

           <bold>pRelationshipTalkAbout</bold> controls how willing the NPC is to divulge

           personal information about the relation. A husband may be wary about divulging

           personal information about his wife, and have high values, like [4, 4]. However,

           NPCs are willing to say anything (bad) they know about enemies,

           with a [-4, -4], score.

    </li>

    <li>

           Setting <bold>pHidden</bold> to TRUE will cause the relationship to

           be secret. It won't be spoken of. The only way the player can learn it is

           by observation.

    </li>

</ul>

 

 

<br/><section><p><bold><big>Minor relationships</big></bold></p></section>

 

<p>

    So far, I've just been discussing "major" relationships. Although no

    "minor" relationships are built in, you may wish to add some. Minor

    relationships are variations on the major ones. For example: You could

    have a "loving" (minor relationship) "spouse" (major relationship). The loving

    spouse might have a higher pRelationshipLike.

</p>

 

<p>

    As you may have noticed, there is an important naming schemes for

    relationship objects. All relationship objects are named with a prefix

    of "oRelationship", followed by the major relationship, and then followed

    by the minior relationship, if one exists. Thus, the spouse relationship

    object is "oRelationshipSpouse", while the loving-spouse object

    is "oRelationshipSpouseLoving".

</p>

 

<p>

    This naming scheme is critical since it's used

    by <bold>RelationshipToObject()</bold> to convert a major (and minor)

    relationship string into a relationship object.

</p>

 

 

 

<br/><section><p><bold><big>Creating your own relationship object</big></bold></p></section>

 

<p>

    To create your own relationship object, such as that of a "priest" to

    his "follower"(s):

</p>

 

<ol>

    <li>

           Create an object based off <bold>cRelationship</bold>.

    </li>

    <li>

           The object should <bold>not be contained</bold> in any other object.

    </li>

    <li>

           Make sure to <bold>name it "oRelationshipXXXYYY"</bold>, where XXX is the

           major relationship, and YYY is the minor relationship, if there is one.

           Thus, your new relationship object would be "oRelationshipPriest".

    </li>

    <li>

           Make sure to fill in all <bold>the relationship properties</bold>, as listed

           above.

    </li>

    <li>

           If the relationship has an inverse, <bold>create

           the inverse object too.</bold> Thus, if you created

           oRelationshipPriest, you'd also need to create oRelationshipFollower.

    </li>

    <li>

           If you have created a <bold>minor relationship</bold>, such as oRelationshipFollowerFanatic,

           then make sure you also have an object that will handle

           the major relationship, such as oRelationshipFollower.

    </li>

</ol>

 

 

 

 

<br/><section><p><bold><big>Assigning relationships to a NPC</big></bold></p></section>

 

<p>

    When you want to assign one or more relationships to a NPC, do the following:

</p>

 

<ul>

    <li>

           Set the NPC's <bold>pAIRelationships</bold> to indicate what relationships

           the NPC has. For example: [ [oNPCWife, "spouse"], [oNPCChild, "child"] ] causes

           the NPC to be married and have one child.

    </li>

    <p>

           Make sure that the <bold>NPC's inverse relationship</bold> is included in

           the pAIRelationships of the NPC's relations. For example: You would need

           to set oNPCChild.pAIRelationships to [[oNPCFather, "parent"]].

    </p>

    <li>

           Additionally, you could write your

           own <bold>AIRelationships()</bold> method. This might be useful to ensure

           that all members of a town have a relationship with the town mayor, etc.

    </li>

    <li>

           You can also create relationship by adding a pAIRelationships

           to <bold>cFaction objects</bold>.

    </li>

</ul>

 

 

 

 

<br/><section><p><bold><big>Inquiring about NPC relationships</big></bold></p></section>

 

<p>

    If you're writing code and wish to find out what kinds of relationships

    a NPC has with other NPCs, you can do the following:

</p>

 

<ul>

    <li>

           Call the NPC's <bold>AIRelationships()</bold> and search through the list

           yourself.

    </li>

    <li>

           Call the NPC's <bold>AIRelationshipExists()</bold> to see if the NPC

           has a relationship with a specific NPC. If the NPC does, then a list of the

           relationship objects will be returned. Alternatively, the NPC may merely

           know the other NPC because they're from the same town, causing a number (in

           this case 2.0) to be returned.

    </li>

    <p>

           The NPC's "home town" is automatically determined based on the NPC's

           creation room. If you wish to specify a specific map (or maps) then

           set <bold>pAIHomeMap</bold>, or write your

           own <bold>AIHomeMap()</bold> property.

    </p>

</ul>

 

 

 

<br/><section><p><bold><big>Player database NPC relationships</big></bold></p></section>

 

<p>

    As playes meet NPCs, a database about the NPC is automatically saved, allowing

    the player to then view relationship graphs of the NPC. Most of the

    relationship information is automatically stored, so you won't need to

    worry about it. However, in the event that you do need to modify the

    relationshipd database:

</p>

 

<ul>

    <li>

           The database is stored in the player

           character's <bold>pKnowledgeRelationships</bold> property. You shouldn't

           need to access this directly.

    </li>

    <li>

           You can find remembered relationship information specific to a NPC

           by calling the PC's <bold>KnowledgeRelationshipsGet()</bold>.

    </li>

    <li>

           To have the player's character remember relationship information,

           call <bold>KnowledgeRelationshipsSet()</bold>. You can store information

           about the fact that a relationship exists between two NPCs, the

           NPC's last like/trust for the player, and the NPC's last known location.

    </li>

</ul>

 

 

<br/><section><p><bold><big>Automatic conversations between relationships</big></bold></p></section>

 

<p>

    You can have automatic conversations happen between two NPCs given a specific

    relationship. This is handy to have friends say hello, enemies sneer at one

    another, etc.

</p>

 

<p>

    To do this, your relationship object should have:

</p>

 

<ul>

    <li>

           <bold>pAIConvScripts</bold> filled in with one or more conversation scripts

           that might occur.

    </li>

</ul>

 

<p>

    Each cConvScript specific to the relationship needs to have:

</p>

 

<ul>

    <li>

           <bold>pConvScriptAutoRelationship</bold> set to the relationship object.

    </li>

    <li>

           <bold>pConvScriptNPCs</bold> set to 2, since relationships only exist

           between two NPCs.

    </li>

    <li>

           <bold>pConvScriptAutoPriority</bold> should be lowered (below 50) so that

           other non-autmatic conversation scripts will take priority.

    </li>

    <li>

           Other <bold>cConvScript</bold> properties as appropriate.

    </li>

    <li>

           You may wish to modify the <bold>pConvScriptResource</bold> resource so

           that the relationship between the two NPCs is automatically exposed.

           That way, players that see the conversation happen will learn of

           the relationship.

    </li>

</ul>

22 Favors

Favors, the conversational-game equivalent of "loot".

 

<section><p><bold><big>Favors</big></bold></p></section>

 

<p>

    Once players have gotten successfully friendly with NPCs (by giving them

    gifts, doing quests for them, etc.), the PCs can ask the NPCs

    for <bold>favors</bold>.

</p>

 

<p>

    "Favors" are the equivalent to "loot" in the "kill 10 rats" game. It acts

    as an incentive/reward, and also enables new gameplay. NPCs can grant

    a variety of favors:

</p>

 

<ul>

    <li>

           <bold>goodword</bold> - The NPC puts in a good word to one of its

           relations, providing the player with a "leg up" when interacting with

           the relation (NPC).

    </li>

    <li>

           <bold>knowledge</bold> - The NPC will tell the player a tasty rumor,

           which the player can then use to get procede elsewhere in the game.

    </li>

    <li>

           <bold>money</bold> - This is a bit mundane, but players can ask

           for a bit of extra cash.

    </li>

    <li>

           <bold>object</bold> - The NPC gives the player an object, like a key (that

           provides access to another part of the world), and a letter of reference

           to another NPC.

    </li>

    <li>

           <bold>reputation</bold> - The PC's reputation (such as how heroic the PC is)

           can be enhanced, affecting how other NPCs react to the PC.

    </li>

    <li>

           <bold>skill</bold> - The NPC trains the PC in a new skill, such as a long-forgotten

           language that's needed to translate an important document.

    </li>

    <li>

           <bold>speak</bold> - The NPC speaks some words of wisdom (such as hints) to

           the player.

    </li>

    <li>

           <bold>story</bold> - The NPC tells the PC a special story.

    </li>

    <li>

           <italic>custom</italic> - Whatever you wish to code.

    </li>

</ul>

 

<p>

    To ask for a favor, the player types "Can you do me a favor?"

    This <bold>automatically</bold> appears on the NPC's context menu.

</p>

 

<p>

    After asking for a favor, the player will be presented with a list of favors,

    and will be able to choose one.

</p>

 

<p>

    After the favor is granted, the player <bold>won't</bold> be able to ask

    for another favor for aroud half a day (of real time), and until the player

    has rebuilt some of the like/trust lost by asking for the favor.

</p>

 

<br/><section><p><bold><big>Coding</big></bold></p></section>

 

<p>

    To get a NPC to provide favors, you need to do the following:

</p>

 

<ol>

    <li>

           <bold>Nothing!</bold> Some defaults already exist for:

    </li>

    <p>

           "goodword" favors are automatically created for all of the NPC's positive (non-enemy)

           relations so long as the player knows about these relations. (See KnowledgeRelationshipsGet()).

    </p>

    <p>

           A "money" favor is automatically created that defaults to giving pAIFavorMoney inflation-adjusted

           units.

    </p>

    <p>

           Some objects, based on pAIGivable, are automatically added by AIFavor().

    </p>

    <li>

           Having said that, you'll probably want to modify <bold>pAIFavor</bold> to provide some

           custom favors.

    </li>

</ol>

 

<p>

    You may also wish to modify:

</p>

 

<ul>

    <li>

           <bold>pAIFavorAsk</bold> is the question that the player asks the NPC to

           be granted a favor. This defaults to "Could you do me a favor?"

    </li>

    <li>

           <bold>pAIFavorLike</bold> controls how many favors a NPC will grant, as well as how

           much the NPC must like/trust the player to grant them.

    </li>

    <li>

           <bold>pAIFavorLikeCost</bold> affects how much the NPC's like/trust goes down

           after a favor has been asked.

    </li>

    <li>

           <bold>pAIFavorQuestion</bold> is what the NPC speaks after the player

           has asked for a favor.

    </li>

    <li>

           <bold>pAIFavorTime</bold> specified the number of real days that the player must

           wait before being able to ask the NPC for another favor.

    </li>

    <li>

           <bold>AIFavorDoCustom()</bold> can be used to write your own custom favor

           behaviors, such as, "Can you quack like a duck for the next two hours?"

    </li>

</ul>

 

<p>

    If you wish a NPC to grant a favor outside of the default context menu,

    then you may wish to call AIFavorDo() and AIFavor().

</p>

 

 

Documentation – Basic fantasy Library

Provides for basic fantasy RPG genre, such as one with Elves and Dwarves.

 

This is a library that includes basic fantasy RPG genre elements, including various races, languages, etc. You can use it to base your world off of, or merely as an example.

 

The fantary library requires the Basic RPG library (and also Basic IF library) to work. You should set the Basic Fantasy library to a higher priority than the Basic RPG library.

 

01 Books, letters, and signs – cBook and friends

How to easily create books.

 

<section><p><bold><big>Books, letters, and signs - cBook and friends</big></bold></p></section>

 

<p>

    Circumreality includes a cBook object that makes it easy to create your

    own books. To create a book:

</p>

 

<ol>

    <li>

           <bold>Create an object</bold> as normal.

    </li>

    <li>

           Sub-class it off of <bold>cBook</bold>.

    </li>

    <li>

           Set <bold>pNLPNounName</bold> and <bold>pNLPParseName</bold> if

           you wan't to use a more creative name for your object than

           just "book".

    </li>

    <li>

           Set <bold>pBookCover</bold> to the title of the book.

    </li>

    <li>

           Set <bold>pBookPages</bold> to the text (and/or graphics) to

           be displayed on each page.

    </li>

    <li>

           Set <bold>pBookLanguage</bold> to the language that the book

           is written in. This defaults to "common".

    </li>

</ol>

 

<p>

    That's it.

</p>

 

 

<br/><section><p><bold><big>Advanced features</big></bold></p></section>

 

<p>

    You may also wish to set the following properties:

</p>

 

<ul>

    <li>

           <bold>pBookAutoFirstPage</bold> causes the book to automatically

           flip back to the first page after awhile. Use this for public

           books so players find them at the start page.

    </li>

    <li>

           <bold>pBookBindingDepth, pBookBindingRotate, pBookBindingWidth,

           pBookLayoutCover, pBookLayoutPageLeft, pBookLayoutPageOffset,

           and pBookLayoutPageRight</bold> should

           be changed if you change the <bold>size or shape</bold> of

           pBookVisualClosed, pBookVisualOpen,

           and/or pBookVisualPage.

    </li>

    <li>

           <bold>pBookCanTear</bold> controls whether a book can be

           torn up or not.

    </li>

    <li>

           <bold>pBookFont</bold> and <bold>pBookFontCover</bold> change the

           font used by the book.

    </li>

    <li>

           <bold>pBookFontFaceUnknownLanguage</bold> is used to use an

           unreadable font (such as "Symbol") when a player character doesn't

           understand the language that the book is in.

    </li>

    <li>

           <bold>pBookNoCover</bold> and <bold>pBookNoCoverViewSingly</bold> turn

           the book into a series of pages.

    </li>

    <li>

           <bold>pBookPageLayoutNumberingLeft and

           pBookPageLayoutNumberingRight</bold> control where the page numbers go.

    </li>

    <li>

           <bold>pBookPageLayoutText</bold> controls where the text goes on the page.

    </li>

    <li>

           <bold>pBookPageOpenTo</bold> affects the page that the book is opened to.

           This must <bold>always</bold> be an even number.

    </li>

    <li>

           <bold>pBookPagesCutScenes</bold> is a list of cut scenes resources for

           each page, causing the "reading" of the book to play a cut scene.

    </li>

    <li>

           <bold>pBookVisualClosed, pBookVisualOpen, and pBookVisualPage</bold> let

           your change the color, size, and shape of the book.

    </li>

</ul>

 

 

 

<p>

    You may wish to write your own methods:

</p>

 

<ul>

    <li>

           <bold>BookPage()</bold> just returns a page from pBookPages by default.

           You probably won't have to override this, but you might think of a reason.

    </li>

    <li>

           <bold>BookPageEncode()</bold> makes it easy to automatically "encode"

           a page so that the players have to decipher it. (Aka: a puzzle)

    </li>

    <li>

           <bold>BookPageLayout()</bold> can be overridden to control the

           layout of a specific page.

    </li>

    <li>

           <bold>BookPageLayoutNumbering()</bold> affects where and how the

           page numbering appears.

    </li>

    <li>

           <bold>BookPageNextPrevious()</bold> is called when the player flips

           to the next/previous page. If you wanted to booby-trap your book,

           you could have it explore when the right page is flipped to.

    </li>

    <li>

           <bold>BookPageRead()</bold> is called when a player asks to read

           the page. By default this reads a cutscene from pBookPagesCutScenes.

    </li>

    <li>

           <bold>BookPages()</bold> returns the number of pages in the book.

    </li>

    <li>

           <bold>BookPageTouch()</bold> is called if a player touches a page

           in the book.

    </li>

    <li>

           <bold>BookVisualGetNoText()</bold> returns the visual of the book

           without any text super-imposed on it.

    </li>

</ul>

 

 

<br/><section><p><bold><big>Books are containers</big></bold></p></section>

 

<p>

    Books are containers, and based off of cContainer. They can't hold

    very much in them though, just other pieces of paper.

</p>

 

<p>

    If an object (piece of paper) is placed in the book, and the

    page is flipped (using BookPageNextPrevious()), then the

    object will be hidden by setting its pHidden to TRUE. The

    object will also have its pSearchBookPage set to the page

    where the object was placed.

</p>

 

<p>

    When a player flips back to that page, the object will be

    unhidden, and the player will be notified of the object.

</p>

 

<p>

    SearchInsideThis() is overridden for books so that a "Search the book"

    command won't find anything. Instead, players will have to flip

    through individual pages and see if anything falls out.

</p>

 

 

 

<br/><section><p><bold><big>Letters and bits of paper</big></bold></p></section>

 

<p>

    You can create letters and bits of paper by:

</p>

 

<ol>

    <li>

           Create an object based on <bold>cPaperSheets</bold> if you wish

           it to be displayed like a book with sheets on the left

           and right, but with no cover,

           or <bold>cPaperSheet</bold> to display a single sheet at a time.

    </li>

    <li>

           <bold>Fill in properties</bold> as per cBook, except don't

           worry about the cover.

    </li>

</ol>

 

 

 

<br/><section><p><bold><big>Signing books (and letters) in cBook</big></bold></p></section>

 

<p>

    You can allow players (or NPCs) to sign books and letters by:

</p>

 

<ol>

    <li>

           Set <bold>pBookSignaturesMax</bold> to the maximum number of signatures

           allowed in the book. This defaults to 0.

    </li>

</ol>

 

 

<p>

    You might also wish to modify the following:

</p>

 

<ul>

    <li>

           You might wish to set <bold>pBookSignaturesPerPage</bold> and <bold>pBookSignaturesLastPage</bold> to

           control how many signatures are on a page.

    </li>

    <li>

           <bold>pBookSignatures</bold> can be pre-loaded with signatures.

    </li>

    <li>

           You may wish to set <bold>pCanManipulateWhenHeldByOther</bold> to TRUE

           so that PCs (or NPCs) can see, manipulate, and sign the book even if its

           held by another PC (or NPC). This setting is particularly useful for petitions

           where a PC can merely show it to another PC and not have to give it.

    </li>

    <li>

           <bold>pBookSignaturesNPCs</bold>, <bold>pBookSignaturesPCs</bold>, and <bold>pBookSignaturesClass</bold> affect

           whether players and/or NPCs are allowed to sign the book.

    </li>

    <li>

           <bold>pBookDefaultAIShowLike</bold> and <bold>pBookDefaultAIShowLikeByClass</bold> control

           how much a NPC must like the PC before they're willing to sign.

    </li>

    <li>

           <bold>pBookDefaultAIShowIfSignLike</bold> and <bold>pBookDefaultAIShowIfNotSignLike</bold> control

           how much the NPC's opinion of the PC is changed by being asked to sign.

    </li>

    <li>

           <bold>pBookDefaultAIShowIfSignSpeak</bold> and <bold>pBookDefaultAIShowIfNotSignSpeak</bold> are

           spoken before the NPC signs, or if the NPC decides not to sign.

    </li>

    <li>

           To produce special reactions, write your

           own <bold>DefaultAIShow()</bold> and <bold>DefaultAIOffer()</bold> methods.

    </li>

</ul>

 

<br/><section><p><bold><big>Signs</big></bold></p></section>

 

<p>

    You can create signs by:

</p>

 

<ol>

    <li>

           Create an object based on <bold>cSign</bold>.

    </li>

    <li>

           Set <bold>pBookPages</bold> to a list with only one

           item (aka: page), the writing on your sign.

    </li>

    <li>

           <bold>Fill in other properties</bold> as per cBook, except don't

           worry about the cover.

    </li>

</ol>

 

 

Documentation – Basic Monsters Library

Library with basic classes and sample monsters.

 

This library contains the basic cMonster class, along with some sample monsters. Use this for monsters, rather than cCharacter, because it creates a character that just attacks player characters and isn't terribly sociable.

 

No tutorial documentation for this library

 

 

Documentation – Extra Objects

Library of extra objects, such as furniture and food items.

 

This is a library of extra objects, such as furniture and food items.

 

No tutorial documentation for this library

 

 

Documentation – Sample world

This library provides sample code for a small world. You may wish to look at this code to see how to create a world.

 

This is a library for a small test world. It's included so you can look at the code to see how to create a world. If you already know how to create a world then you probably don't want this included in your project.

 

To get the test world to work:

 

1) Make sure you have installed CircumReality into c:\program files\mXac\CircumReality. If you installed to a different location then the resources won't load properly.

 

2) Select Misc, Compile.

 

3) Select Misc, Test Compiled code

 

4) A window will appear showing all the players that are logged in and playing at the moment; of course, this will be empty.

 

5) In the directory where you have saved your project (that you're working on now), you'll see a file, YourProjectName.crk. "YourProjectName" will be replaced by the filename you used for this project file.

 

6) Open the client, CircumReality.exe, and when selecting the world to play in, select YourProjectName.crk from whatever directory it's save in. (You can also hand this out to your friends so they can log in too.)

 

7) When you play using the YourProjectName.crk world-link, you'll be asked for an IP address to use. You can find the IP address by looking at the title-bar of the server window, the one that lists all current players. It'll show your current IP, or 127.0.0.1 if you're not currently connected to the internet.

 

8) That's all you need to get started.

 

 

No tutorial documentation for this library

 

 

 

 

Library – Standard Library

Overview

This library contains many important functions for the language.

The standard library contains many basic definitions for the language, including built-in methods for strings, lists, and those common to all objects. It also includes commonly used functions like rand(), sin(), etc.

 

As a general rule, you need to include this library in your project.

List of Strings

None

 

List of Resources

None

 

Method Definitions

ClassEnum

Returns a list of classes that this object is a subclass of.

 

This returns a list of classes that this object is a subclass of.

 

For example, if "dollarBill" is a subclass of "money" and "paper" then DollarBill.ClassEnum() returns ["DollarBill", "money", "paper"].

 

 

Parameter Name

Description

Return value description

List of classes that the object is a member of.

 

Overrides: Call only the highest priority method

Common to all objects

 

ClassQuery

Returns TRUE of an object is a member of the given class.

 

This returns TRUE of an object is a member of the given class.

 

For example, if "dollarBill" is a subclass of "money" and "paper" then DollarBill.ClassQuery("money") returns TRUE.

 

Parameter Name

Description

Class

This is the class to look query. It must be a string.

Return value description

TRUE is the object is a subclass of the class, or FALSE if it isn't.

 

Overrides: Call only the highest priority method

Common to all objects

Clone

Duplicates the object.

 

This duplaces the object.

 

Example:

 

string = "Hello Mike!";

string2 = string.Clone();

string.StringAppend ("test");

 

Results:

string == "Hello Mike!test";

string2 == "Hello Mike!"

 

 

Parameter Name

Description

Return value description

The cloned object.

 

Overrides: Call only the highest priority method

Common to all objects

 

Constructor

Automatically run immediately after an object is created.

 

If an object supports the Constructor() method then when the object is created, the Constructor() method will automtically be called.

 

If an object has one or more superclasses with Constructor()'s the superclass's constructors will be called first, working its way up the chain to the object's Constructor().

 

NOTE: The object should NOT delete itself in its own Constructor().

 

 

Parameter Name

Description

Return value description

None

 

Overrides: Call all the methods, from lowest to highest priority – don’t stop

Common to all objects

 

 

Constructor2

Automatically run immediately after an object is reloaded.

 

If an object supports the Constructor2() method then when the object is reloaded from a saved session (NOT created), the Constructor2() method will automtically be called.

 

If an object has one or more superclasses with Constructor2()'s the superclass's constructors will be called first, working its way up the chain to the object's Constructor2().

 

NOTE: The object should NOT delete itself in its own Constructor2().

 

Parameter Name

Description

Return value description

None

 

Overrides: Call all the methods, from lowest to highest priority – don’t stop

Common to all objects

 

ContainedInGet

Returns the object that contains this object.

 

This returns the object that contains the current object.

 

For example: If the "money" object is contained within the "wallet" object it will return the wallet object.

 

Parameter Name

Description

Return value description

The object that contains this object, or NULL if it isn't contained.

 

Overrides: Call only the highest priority method

Common to all objects

 

ContainedInSet

Moves the object into another object.

 

This moves the object so it's contained by another object.

 

For example:

 

money.ContainedInSet (wallet);

return money.ContainedInGet();

 

This resturns "wallet".

 

Parameter Name

Description

MoveTo

The object to move this one into. This can either be an object or NULL (which will cause the object to not be contained in any other object.)

Return value description

TRUE is the move succedes, FALSE if it fails (perhaps because the container object no longer exists.)

 

Overrides: Call only the highest priority method

Common to all objects

 

ContainsEnum

Returns a list of objects that this object contains.

 

This returns a list of objects that are contained by this object.

 

For example:

 

money.ContainedInSet (wallet);

return wallet.ContainsEnum();

 

This returns [money].

 

Parameter Name

Description

Return value description

List of objects that this object contains. The list may be empty if the object contains nothing.

 

Overrides: Call only the highest priority method

Common to all objects

 

DeleteWithContents

Deletes the object along with all its contained objects.

 

This deletes the object along with all the object it contains, and the ones they contain.

 

If you just with to delete the object by itself (and leave the contained objects around) then call "delete <object>".

 

Parameter Name

Description

Return value description

TRUE is deleting succeded, FALSE if it failed.

 

Overrides: Call only the highest priority method

Common to all objects

 

Destructor

Automatically run immediately before the object is destroyed.

 

If an object supports the Destructor() method then when the object is deleted, the Destructor() method will automtically be called.

 

If an object has one or more superclasses with Destructor()'s the object's destructor will be called first, working its way down to the superclasses.

 

When an application is quickly shut down, the destructors of the remaining objects may not be called (to speed up shutdown.)

 

NOTE: The object should NOT delete itself in its own Destructor().

 

Parameter Name

Description

Return value description

None

 

Overrides: Call all the methods, from highest to lowest priority – don’t stop

NOT Common to all objects

 

LayerAdd

Adds a new layer to an object.

 

This adds a new layer to an object. The index of the layer (for purposes of LayerGet(), etc.) will depend upon the priority.

 

Adding a new layer will add all of the methods of the class to the object. It will NOT add the properties of the class, although if the class provides any property get/set code, that will be added.

 

Example:

 

Object.LayerAdd ("LayerName", "LayerClass", 43.54);

 

Parameter Name

Description

LayerName

String to identify the layer. The name is only to make it easier for the application to monitor the layers.

LayerClass

String representation of the layer class. If the class isn't valid the layer will not be created.

LayerPriority

Priority for the layer. Layers are ordered such that the ones with the highest priorities have their methods take precedence over the others.

Return value description

TRUE if it succedes. FALSE if there's an error (such as a bad layer class).

 

Overrides: Call only the highest priority method

Common to all objects

 

LayerGet

Gets information about a layer.

 

This returns information about a layer: its name, class, and priority.

 

Example:

 

return Object.LayerGet (0);

 

Returns ["LayerName", "LayerClass", 43.54].

 

Parameter Name

Description

LayerNum

Layer number, from 0 to LayerNumber()-1.

Return value description

This returns a list. List[0] is the layer name. List[1] is the layer's class, as a string. List[2] is the layer's rank; higher ranks will always appear first on the list.

 

Overrides: Call only the highest priority method

Common to all objects

LayerMethodAdd

Adds an individual method to a layer.

 

This adds an individual method to a layer. Applications can use this to dynamically create methods for objects.

 

Example:

 

Object.LayerMethodAdd (0, "MyNewMethod", Trace);

 

This adds a new method to layer 0, called "MyNewMethod". When the method is called, it really calls into the code of the function, "Trace".

 

Parameter Name

Description

LayerNum

Layer number, from 0 to LayerNumber()-1.

NewMethodName

The name of the new method. This must be a string (with a valid method name) or a method.

CallThis

This is the code that will be run when the method is called. It must either be the name of an existing function or method. If it's a method, the method must exist in the object; it can come from another object if "Object.Method" is used to specify the object.

Return value description

TRUE if the method was added, FALSE if there's an error.

 

Overrides: Call only the highest priority method

Common to all objects

LayerMethodEnum

Returns a list of methods in the layer.

 

This returns a list of methods in the layer.

 

If it is enumerating class-defined methods then it only enumerates the public ones. If enumerating invidually added methods then it enumerates all of them.

 

Example:

 

return Object.LayerMethodEnum(0, TRUE);

 

Parameter Name

Description

LayerNum

Layer number, from 0 to LayerNumber()-1.

EnumClass

If TRUE then enumerate all the public methods from the layer's class. If FALSE then enumerate all individually added methods (using LayerMethodAdd()).

Return value description

List of methods, or NULL if an error occurs.

 

Overrides: Call only the highest priority method

Common to all objects

 

LayerMethodRemove

Removes an individual method to a layer.

 

This removes an individual method to a layer. Applications can use this to dynamically remove methods for objects.

 

Example:

 

Object.LayerMethodRemove (0, "MyNewMethod");

 

The removes "MyNewMethod" which was added by calling LayerMethodAdd().

 

Parameter Name

Description

LayerNum

Layer number, from 0 to LayerNumber()-1.

RemoveMethodName

This removes an individual method to a layer. Applications can use this to dynamically remove methods for objects.

 

Example:

 

Object.LayerMethodRemove (0, "MyNewMethod");

 

The removes "MyNewMethod" which was added by calling LayerMethodAdd().

Return value description

TRUE if the method was removed, FALSE if there's an error.

 

Overrides: Call only the highest priority method

Common to all objects

 

LayerNumber

Returns the number of layers in an object.

 

This returns the number of layers in an object.

 

Parameter Name

Description

Return value description

The number of layers in an object.

 

Overrides: Call only the highest priority method

Common to all objects

 

LayerPropGetSetAdd

Adds code for getting or setting a property to a layer.

 

This adds code for getting or setting an individual property to a layer. Applications can use this to dynamically create code to intercept property get and set for an object. (If no code exists then the property is accessed directly.)

 

Example:

 

Object.PropertySet ("MyNewProp", 0);

Object.LayerPropGetSetAdd (0, "MyNewProp", GetFunc, SetFunc);

 

After this is called, any attempts to change "MyNewProp" will be passed to SetFunc(), and any attemps to get the value will be passed to GetFunc().

 

Parameter Name

Description

LayerNum

Layer number, from 0 to LayerNumber()-1.

PropertyName

The name of the property. This must be a string.

 

If the method already exists in the layer's individual methods then an error will be returned.

GetCode

This is the code that will be run when the property is read from. It must either be the name of an existing function or method. If it's a method, the method must exist in the object; it can come from another object if "Object.Method" is used to specify the object.

 

If this is NULL the property will be accessed directly when it's read.

SetCode

                                  This is the code that will be run when the property is set. It must either be the name of an existing function or method. If it's a method, the method must exist in the object; it can come from another object if "Object.Method" is used to specify the object.

 

If this is NULL the property will be accessed directly when it's written.

Return value description

The number of layers in an object.

 

Overrides: Call only the highest priority method

Common to all objects

 

LayerPropGetSetEnum

Returns a list of properties supported by the layer.

 

This returns a list of properties supported by the layer.

 

If it is enumerating class-defined properties then it only enumerates the public ones. If enumerating invidually added properties then it enumerates all of them.

 

Example:

 

return Object.LayerPropGetSetEnum(0, TRUE);

 

Parameter Name

Description

LayerNum

Layer number, from 0 to LayerNumber()-1.

EnumClass

If TRUE then enumerate all the public properties from the layer's class. If FALSE then enumerate all individually added properties (using LayerPropGetSetAdd()).

Return value description

List of properties (as strings), or NULL if an error occurs.

 

Overrides: Call only the highest priority method

Common to all objects

LayerPropGetSetRemove

Removes an individual property from a layer.

 

This removes an individual property from a layer. Applications can use this to dynamically remove property get/set code for objects.

 

Example:

 

Object.LayerPropGetSetRemove (0, "MyNewProp");

 

The removes "MyNewProp" which was added by calling LayerPropGetSetAdd().

 

Parameter Name

Description

LayerNum

Layer number, from 0 to LayerNumber()-1.

PropertyName

The name of the property to remove. This must be a string.

Return value description

TRUE if the property was removed, FALSE if there's an error.

 

Overrides: Call only the highest priority method

Common to all objects

LayerRemove

Removes a layer from an object.

 

This removes a layer from an object. Removing the layer will also remove methods provided for in that layer.

 

Parameter Name

Description

LayerNum

Layer number, from 0 to LayerNumber()-1.

Return value description

TRUE if the layer was removed, FALSE if there was an error.

 

Overrides: Call only the highest priority method

Common to all objects

ListAppend

Appends a list to the end of another.

 

This appends a list's elements to the end of the current list. Replacement is IN PLACE, causing the original list to be modified.

 

Example:

 

list = [1,2,3,[5,6]];

string.ListAppend ([8,9]);

 

The resulting list is [1,2,3,[5,6],8,9]

 

Parameter Name

Description

Insert

(Optional) What to append.

Return value description

The list.

 

Overrides: Call only the highest priority method

Common to all objects

ListConcat

Appends one or items  to the end of the string list.

 

This concatenates one or more items onto the end of the current list. Any lists added are created as sub-lists. The current list is modified in place.

 

Example:

 

var list = [1,2,3];

list.ListConcat (4, [5,6]);

 

results in:

 

list = [1,2,3,4,[5,6]]

 

Parameter Name

Description

Concat

First tiem to concatenate to the list. More than one item can be added.

Return value description

The list.

 

Overrides: Call only the highest priority method

Common to all objects

ListInsert

Inserts a list before the element of another.

 

This inserts a list's elements before an element in the current list. Replacement is IN PLACE, causing the original list to be modified.

 

Example:

 

list = [1,2,3,[5,6]];

string.ListInsert ([8,9], 1);

 

The resulting list is [1,8,9,2,3,[5,6]]

 

If AddSubLists is TRUE then the result would be: [1,[8,9],2,3,[5,6]]

 

Parameter Name

Description

Insert

(Optional) What to insert.

StartIndex

(Optional) Element to insert before. If not specified then this will be 0.

AddSubLists

(Optional) If this is TRUE then if the Insert parameter is a list the list will be added as a sublist. If FALSE (or undefined) then if Insert is a list its individual elements will be added.

Return value description

The list.

 

Overrides: Call only the highest priority method

NOT Common to all objects

ListMerge

Merges the second list onto the first.

 

This merges a second list onto the end of the first. The current list is modified in place.

 

Example:

 

var list = [1,2,3];

list.ListConcat ([5,6]);

 

results in:

 

list = [1,2,3,5,6]

 

Parameter Name

Description

Concat

List that gets merged onto the end of the current list. If this is not a list then the item will be merged into the list.

Return value description

The list.

 

Overrides: Call only the highest priority method

NOT Common to all objects

ListNumber

Returns the number of items in the list.

 

This returns the number of items in the list.

 

Example:

 

var list = [1,2,3,[5,6]];

return list.ListNumber();

 

Returns 4.

 

Parameter Name

Description

Return value description

The number of items in the list.

 

Overrides: Call only the highest priority method

NOT Common to all objects

ListPrepend

Prepends a list to the beginning of another.

 

This prepends a list's elements to the start of the current list. Replacement is IN PLACE, causing the original list to be modified.

 

Example:

 

list = [1,2,3,[5,6]];

string.ListPrepend ([8,9]);

 

The resulting list is [8,9,1,2,3,[5,6]]

 

Parameter Name

Description

Insert

(Optional) What to prepend.

Return value description

The list.

 

Overrides: Call only the highest priority method

NOT Common to all objects

ListRandomize

Randomizes the orders of the elements in the list.

 

This randomizes the order of the elements in the list. The list is modified in place.

 

NOTE: This uses the Random() function for generating the random numbers. If your code has been relying on a random seed this will change the seed.

 

Example:

 

var list = [1,2,3,[5,6]];

list.ListRandomize();

 

Parameter Name

Description

Return value description

The list.

 

Overrides: Call only the highest priority method

NOT Common to all objects

ListRemove

Removes one or more items from the list.

 

This removes one or more items from the list. The current list is modified in place.

 

Example:

 

var list = ["a", "hello", "c", "e"];

list.ListRemove (1, 3);

 

results in:

 

list = ["a", "e"];

 

Parameter Name

Description

StartIndex

First item (by index) to remove from the list.

EndIndex

(Optional) One more than the last item to remove from the list. If this is left blank then EndIndex is set to StartIndex+1, which causes it to only delete StartIndex.

Return value description

The list.

 

Overrides: Call only the highest priority method

NOT Common to all objects

ListReplace

Replaces a portion of one list with another.

 

This replaces a portion of the current list with another. Replacement is IN PLACE, causing the original list to be modified.

 

Example:

 

list = [1,2,3,[5,6]];

string.ListReplace ([8,9], 1, 2);

 

The resulting list is [1,8,9,3,[5,6]]

 

Parameter Name

Description

ReplaceWith

(Optional) What to replace the range with.

StartIndex

(Optional) Starting index to replace. If not specified then this will be 0.

EndIndex

(Optional) Ending index to replace. If not specified this will be the end of the list.

Return value description

The list.

 

Overrides: Call only the highest priority method

NOT Common to all objects

ListReverse

Reverses the elements in the list.

 

This reverses the elements in the list. The list is modified in place.

 

Example:

 

var list = [1,2,3,[5,6]];

list.ListReverse();

 

Results:

 

list = [[5,6],3,2,1]

 

Parameter Name

Description

Return value description

The list.

 

Overrides: Call only the highest priority method

NOT Common to all objects

ListSearch

Searches through a sorted list for an element.

 

This searches through a sorted list for an element. It returns the index to the element, or -1 if it can't be found.

 

The list have been sorted using ListSort() with the same SortingFunction that's passed in.

 

Example:

 

list = ["a", "e", g", "z"];

return string.ListSearch ("e");

 

This returns 1.

 

Parameter Name

Description

SearchFor

The element to look for.

SortingFunction

(Optional) This is a function or method that will sort the elements. The function or method accepts two parameters, A and B. It should return a negative number it A appears before B in the list, 0 if A is the same as B, and 1 if A appears after B in the list.

 

If no sorting function is passed then the list will be sorted alphabetically or numerically. Make sure that all the elements are the same type; don't mix numbers with strings, etc.

Return value description

If the element is found, this is an index into the list. If more than one element matches than an arbitrary one will be returned. If an element isn't found then -1 is returnd.

 

Overrides: Call only the highest priority method

NOT Common to all objects

ListSearchToInsert

Searches through a sorted list for an element to insert before.

 

This searches through a sorted list for an element to insert before. It can be used to keep the sorted even when inserting new items.

 

Example:

 

list = ["a", "e", "g", "z"];

list.ListInsert ("h", list.ListSearchToInsert("h"));

 

The new list will be ["a", "e", "g", "h", "z"];

 

Parameter Name

Description

SearchFor

The element to look for.

SortingFunction

(Optional) This is a function or method that will sort the elements. The function or method accepts two parameters, A and B. It should return a negative number it A appears before B in the list, 0 if A is the same as B, and 1 if A appears after B in the list.

 

If no sorting function is passed then the list will be sorted alphabetically or numerically. Make sure that all the elements are the same type; don't mix numbers with strings, etc.

Return value description

Index in the list to insert before in order to keep the list sorted.

 

Overrides: Call only the highest priority method

NOT Common to all objects

ListSlice

Copies a portion of the list into a new list.

 

This copies a portion of the list into a new list.

 

Example:

 

list  = [1,2,3,[5,6]];

return list.ListSlice (1, -1);

 

Returns [2,3].

 

Parameter Name

Description

StartIndex

The index into the list that will become the start of the new list. If this is negative, this is the index from the end of the list. (ListNumber() + StartIndex).

EndIndex

(Optional) The index into the list beyond the end of the list. This must always be 0 or a negative value, with 0 keeping from StartIndex to the end of the list, -1 discarding the last element, -2 discarding the last two, etc. If this isn't specified then 0 (the end of the list) will be used.

Return value description

The sliced out list.

 

Overrides: Call only the highest priority method

NOT Common to all objects

ListSort

Sorts the list.

 

This sorts the list. Sorting is in place, so the current list is changed.

 

Example:

 

list = [6,2,3,1,4];

string.ListSort();

 

The resulting list is [1,2,3,4,6]

 

Parameter Name

Description

SortingFunction

(Optional) This is a function or method that will sort the elements. The function or method accepts two parameters, A and B. It should return a negative number it A appears before B in the list, 0 if A is the same as B, and 1 if A appears after B in the list.

 

If no sorting function is passed then the list will be sorted alphabetically or numerically. Make sure that all the elements are the same type; don't mix numbers with strings, etc.

Return value description

The list.

 

Overrides: Call only the highest priority method

NOT Common to all objects

ListSubList

Copies a portion of the list into a new list.

 

This copies a portion of the list  into a new list.

 

Example:

 

list = [1,2,3,[5,6]];

return list.ListSubList(1,3);

 

Returns [2,3].

 

Parameter Name

Description

StartIndex

The index into the list that will become the start of the new list.

EndIndex

(Optional) The index into the list that will become end of the new list. If this isn't specified then the end of the list will be used.

Return value description

The sliced out list.

 

Overrides: Call only the highest priority method

NOT Common to all objects

MethodCall

Calls a method in an object.

 

This calls a method in an object. It can be used to call methods using a string instead of the method.

 

NOTE: This only calls public methods.

 

 

For example:

 

return AnObject.MethodCall ("AMethod", 3, 5);

 

Parameter Name

Description

Method

Method name. This is either a method name or a string.

Param1

Parameters passed into the method.

Return value description

Whatever the method returns, or Undefined if the method doesn't exist.

 

Overrides: Call only the highest priority method

Common to all objects

MethodEnum

Returns a list of the methods supported by the object.

 

This returns a list of methods supported by the object.

 

NOTE: This only returns public methods.

 

 

For example:

return AnObject.MethodEnum ();

 

Returns [Method1, Method2, etc.]

 

Parameter Name

Description

Return value description

List of the methods supported. This does not include automatic methods, such as MethodEnum(). If no methods are supported by the object it returns an empty list.

 

Overrides: Call only the highest priority method

Common to all objects

MethodQuery

Returns TRUE if the object supports the method.

 

This checks to see if an object supports a method and returns TRUE if it exists.

 

NOTE: This only checks public methods.

 

 

For example:

 

return AnObject.MethodQuery ("AMethod");

 

Parameter Name

Description

Method

Method name. This is either a method name or a string.

Return value description

TRUE if the method exists, FALSE if it doesn't.

 

Overrides: Call only the highest priority method

Common to all objects

Name

Returns the name of an object.

 

This returns the name of an object. It can called by any function or method.

 

It is automatically called when an object is concatenated to a string so that a proper name can be concatenated. If unsupported, the class that the object was originally created from will be used as the name.

 

Parameter Name

Description

Return value description

Should return a string for the name of the object.

 

Overrides: Call only the highest priority method

Common to all objects

PropertyEnum

Returns a list of the properties supported by the object.

 

This returns a list of the properties supported by the object.

 

NOTE: This only returns public properties.

 

 

For example:

return AnObject.PropertyEnum ();

 

Returns ["AProperty", "Property2", etc.]

 

Parameter Name

Description

Return value description

List of the properties supported. If no properties are supported by the object it returns an empty list.

 

Overrides: Call only the highest priority method

Common to all objects

PropertyGet

Gets an object's property using a string for the property name.

 

The gets and object's property, using a string for a property name.

 

NOTE: This only gets public properties.

 

 

For example:

 

AnObject.AProperty = 5;

return AnObject.PropertyGet ("AProperty");

 

Returns 5.

 

Parameter Name

Description

Property

Property name. This must be a string.

Return value description

Value of the property, or Undefined if it wasn't supported by the object.

 

Overrides: Call only the highest priority method

Common to all objects

PropertyQuery

Returns TRUE if the object supports the property.

 

This checks to see if an object supports a property and returns TRUE if it exists.

 

NOTE: This only checks public properties.

 

 

For example:

 

AnObject.AProperty = 5;

AnObject.PropertyRemove ("AProperty");

return AnObject.PropertyQuery ("AProperty");

 

Returns FALSE.

 

Parameter Name

Description

Property

Property name. This must be a string.

Return value description

TRUE if the property exists, FALSE if it doesn't.

 

Overrides: Call only the highest priority method

Common to all objects

PropertyRemove

Removes an object's property.

 

The deletes the object's property, using a string for a property name.

 

NOTE: This only removes public properties.

 

 

For example:

 

AnObject.AProperty = 5;

AnObject.PropertyRemove ("AProperty");

 

Results: AProperty is removed from the object.

 

Parameter Name

Description

Property

Property name. This must be a string.

Return value description

TRUE if the property is removed, FALSE if it isn't found.

 

Overrides: Call only the highest priority method

Common to all objects

 

PropertySet

Sets an object's property using a string for the property name.

 

The sets and object's property, using a string for a property name. If the property doesn't already exist in the object it's created.

 

NOTE: This only sets public properties.

 

 

For example:

 

AnObject.PropertySet ("AProperty", 5);

return AnObject.AProperty;

 

Returns 5.

 

Parameter Name

Description

Property

Property name. This must be a string.

Value

What value to set the property as.

Return value description

Value of the property, or undefined if there is an error (such as the property name already used by a different class of identifier).

 

Overrides: Call only the highest priority method

Common to all objects

StringAppend

Appends a string to the current one.

 

This appends the Insert string at the end of the current string. Insertion is IN PLACE, causing the original string to be modified.

 

Example:

 

string = "Hello";

string.StringAppend (" ! ");

 

The resulting string is "Hello ! ".

 

Parameter Name

Description

Insert

The string to append.

Return value description

The string.

 

Overrides: Call only the highest priority method

NOT Common to all objects

StringCompare

Compares two strings.

 

This compares the string with the Compare string. It returns 0 if the two strings are the same, a negative value if the string (this) should be placed before Compare, and a positive value if it should be placed after Comapre.

 

Example:

 

string = "abacus";

return string.StringCompare ("zebra");

 

Returns a negative number.

 

Parameter Name

Description

Compare

String to compare against.

CaseSensative

                     (Optional) If TRUE is passed then the comparison will be case sensative; if FALSE it's case insensative. If not specified then the comparison is case sensative.

Return value description

It returns 0 if the two strings are the same, a negative value if the string (this) should be placed before Compare, and a positive value if it should be placed after Comapre.

 

Overrides: Call only the highest priority method

NOT Common to all objects

StringConcat

Appends one or more strings to the end of the string object.

 

This concatenates one or strings onto the end of the current string. The current string is modified in place.

 

Example:

 

var string = "Hello";

string.StringConcat (" there ", 534, ".");

 

results in:

 

string == "Hello there 543."

 

Parameter Name

Description

Concat

First string (or other value) to concatenate. More than one string can be added.

Return value description

The string.

 

Overrides: Call only the highest priority method

NOT Common to all objects

StringFormat

Replaces %1, %2, etc. in the string.

 

This searches through the string for "%1", "%2", ... "%9" and replaces them with the 1st, 2nd, ... 9th parameter in the StringFormat() call.

 

StringFormat() is extremely useful for fill-in-the-blank strings.

 

 

Example:

 

var string = "My %2 is %1.";

return string.StringFomat ("Mike", "name");

 

return "My name is Mike."

 

Parameter Name

Description

Param1

If %1 is found, it is replaced by Param1.

Param2

If %2 is found, it is replaced by Param2.

Return value description

New string that includes the replacements.

 

Overrides: Call only the highest priority method

NOT Common to all objects

StringFromChar

Creates a string from one or more characters or character codes.

 

This creates a string from one or more characters ('a', 'b', etc.) or character codes (numbers).

 

Example:

 

var string;

string = string.StringFromCar (163, '2', '0', '0', '4');

 

results in, the Pound-sterling symbol followed by "2004"

 

Parameter Name

Description

char

The first character or chracter value to be placed in the string. More than one character can be used.

Return value description

New string that includes the replacements.

 

Overrides: Call only the highest priority method

NOT Common to all objects

StringInsert

Inserts one string into another.

 

This inserts the Insert string into the current string. Insertion is IN PLACE, causing the original string to be modified.

 

Example:

 

string = "Hello";

string.StringInsert (" ! ", 1);

 

The resulting string is "H ! ello".

 

Parameter Name

Description

Insert

(Optional) The string to insert.

StartIndex

(Optional) Character index to insert before. If not specified then this will be 0.

Return value description

The string.

 

Overrides: Call only the highest priority method

NOT Common to all objects

StringLength

Returns the number of characters in the string.

 

This returns the number of characters in the string.

 

Example:

 

string = "Hi there!";

return string.StringLength();

 

Returns 9.

 

Parameter Name

Description

Return value description

Number of characters in the string.

 

Overrides: Call only the highest priority method

NOT Common to all objects

StringPrepend

Inserts a string at the beginning.

 

This inserts the Insert string at the beginning of the current string. Insertion is IN PLACE, causing the original string to be modified.

 

Example:

 

string = "Hello";

string.StringPrepend (" ! ");

 

The resulting string is " ! Hello".

 

Parameter Name

Description

insert

The string to insert.

Return value description

The string.

 

Overrides: Call only the highest priority method

NOT Common to all objects

StringReplace

Replaces a portion of one string with another.

 

This replaces a portion of the current string with another. Replacement is IN PLACE, causing the original string to be modified.

 

Example:

 

string = "Hello";

string.StringReplace (" ! ", 1, 2);

 

The resulting string is "H ! llo".

 

Parameter Name

Description

ReplaceWith

(Optional) What to replace the range with.

StartIndex

(Optional) Starting character to replace. If not specified then this will be 0.

EndIndex

(Optional) Ending character to replace. If not specified this will be the end of the string.

Return value description

The string.

 

Overrides: Call only the highest priority method

NOT Common to all objects

StringSearch

Finds an occurence of one string inside another.

 

This searches for the substring within the original string. If it's found then it returns the index into the original string where the substring begins, or -1 if it can't be found.

 

Example:

 

string = "Hi there!";

return string.StringSearch ("there");

 

Returns 3.

 

Parameter Name

Description

SubString

The string to search for.

StartIndex

(Optional) The character index, within SubString, to search from. If this isn't specified then 0 will be used.

CaseSensative

(Optional) If TRUE is passed then the comparison will be case sensative; if FALSE it's case insensative. If not specified then the comparison is case sensative.

Return value description

The index into the string where SubString appears. If SubString is not found then -1 is returned.

 

Overrides: Call only the highest priority method

NOT Common to all objects

StringSlice

Copies a portion of the string into a new string.

 

This copies a portion of the stirng into a new string.

 

Example:

 

string = "Hi there!";

return stirng.StringSlice (3,-1);

 

Returns "there".

 

Parameter Name

Description

StartIndex

The index into the string that will become the start of the new string. If this is negative, this is the index from the end of the string. (StringLength() + StartIndex).

EndIndex

(Optional) The index into the string beyond the end of the string. This must always be 0 or a negative value, with 0 keeping from StartIndex to the end of the string, -1 discarding the last character, -2 discarding the last two, etc. If this isn't specified then 0 (the end of the string) will be used.

Return value description

The sliced out string.

 

Overrides: Call only the highest priority method

NOT Common to all objects

StringSplit

Splits the string into multiple strings based on the delimiter.

 

This splits the string into multiple strings based on the delimiter (such as ',' or ' '). The new strings are added to a list, which is returned.

 

Example:

 

string = "1,2,3,,,5,6,7";

return string.StringSplit (',');

 

Returns ["1", "2", "3", "", "", "5", "6", "7"].

 

Parameter Name

Description

Delimiter

The string or character used to separate the entries.

Return value description

The list containing the split-up string.

 

Overrides: Call only the highest priority method

NOT Common to all objects

StringSubString

Copies a portion of the string into a new string.

 

This copies a portion of the stirng into a new string.

 

Example:

 

string = "Hi there!";

return stirng.StringSubString (3,8);

 

Returns "there".

 

Parameter Name

Description

StartIndex

The index into the string that will become the start of the new string.

EndIndex

(Optional) The index into the string that will become end of the new string. If this isn't specified then the end of the string will be used.

Return value description

The sliced out string.

 

Overrides: Call only the highest priority method

NOT Common to all objects

StringToLower

Converts the string to lower case.

 

Converts the string to lower case.

 

Example:

 

string = "Hello Mike!";

return string.StringToLower();

 

Returns"hello mike!".

 

Parameter Name

Description

Return value description

Lower case version of the string.

 

Overrides: Call only the highest priority method

NOT Common to all objects

StringToUpper

Converts the string to upper case.

 

Converts the string to upper case.

 

Example:

 

string = "Hello Mike!";

return string.StringToUpper();

 

Returns"HELLO MIKE!".

 

Parameter Name

Description

Return value description

Upper case version of the string.

 

Overrides: Call only the highest priority method

NOT Common to all objects

StringTrim

Trims whitespace from the beginning and end of the string.

 

This trims the whitespace from the beginning and end of the string.

 

Example:

 

string = "  Hello Mike!  ";

string.Trim();

 

String's new value is "Hello Mike!"

 

Parameter Name

Description

TrimOnlyLeft

(Optional) This this isn't specified then whitespace on the left and right sides of the string is cleared. If TRUE then only the left whitespace is cleared. If FALSE then only the right whitespace is cleared.

Return value description

The string.

 

Overrides: Call only the highest priority method

NOT Common to all objects

TimerAdd

Adds a new timer to the object.

 

This adds a new timer to the object. If the timers in the object are suspended then this timer will be added in a suspended state too.

 

Example:

 

Object.TimerAdd ("MyTimer", TRUE, 1.5, MyMethod, Param1, Param2);

 

Parameter Name

Description

Name

The name of the timer. Usually this is a string, but it can be any value. If the name is already used for a timer then this call will fail and return FALSE. (NOTE: Name comparisons are CASE SENSATIVE.)

Repeat

If TRUE then the timer keeps repeating until it is removed using TimerRemove(). If FALSE then the timer only runs once before it is automatically removed.

Interval

The number of seconds (or fraction) before the timer goes off. If this is a repeating timer then this is also the number of seconds between repetitions.

Call

This is the function or method to call when the timer goes off.

CallParams

(Optional) Zero or more parameters passed into the function (or method) when the timer goes off.

Return value description

TRUE if the timer was added, FALSE if an error occured.

 

Overrides: Call only the highest priority method

Common to all objects

TimerEnum

Enumerates all the timers in an object.

 

This enumerates all the timers in an object. It returns a list with the timers names.

 

Example:

 

Object.TimerAdd ("MyTimer", TRUE, 1.5, MyMethod, Param1, Param2);

return Object.TimerEnum();

 

Returns ["MyTimer"].

 

Parameter Name

Description

Return value description

List of all the timers in the object. This may be an empty list if there are no timers.

 

Overrides: Call only the highest priority method

Common to all objects

TimerQuery

Deletes a timer from an object.

 

This deletes a timer from an object.

 

Example:

 

Object.TimerAdd ("MyTimer", TRUE, 1.5, MyMethod, Param1, Param2);

Object.TimerRemove ("MyTimer");

 

Parameter Name

Description

Name

The name of the timer, as passed into TimerAdd(). This is a CASE SENSATIVE compare.

Return value description

TRUE if the timer was found and removed. FALSE if the timer wasn't found.

 

Overrides: Call only the highest priority method

Common to all objects

TimerRemove

Deletes a timer from an object.

 

This deletes a timer from an object.

 

Example:

 

Object.TimerAdd ("MyTimer", TRUE, 1.5, MyMethod, Param1, Param2);

Object.TimerRemove ("MyTimer");

 

Parameter Name

Description

Name

The name of the timer, as passed into TimerAdd(). This is a CASE SENSATIVE compare.

Return value description

TRUE if the timer was found and removed. FALSE if the timer wasn't found.

 

Overrides: Call only the highest priority method

Common to all objects

TimerSuspendGet

Returns 0.0 if the object's timers are suspended, 1.0 if they're active, or different values if they're slowed down.

 

Returns 0.0 if the object's timers are suspended.

 

Returns 1.0 if the object's timers are active.

 

The object's timers might also be slowed down (a value between 0.0 and 1.0) or sped up (above 1.0).

 

See also TimerSuspendedSet().

 

Parameter Name

Description

Return value description

Returns TRUE if the object's timers are suspended, FALSE if they are active.

 

Overrides: Call only the highest priority method

Common to all objects

 

TimerSuspendSet

Suspends, resumes, or changes the speed of an object's timers.

 

This suspends, resumes, or changes the speed of an object's timers.

 

An application can use this to quickly and easily suspend timers for an object when the object is inactive. Or, the object's timers can be slowed down when it doesn't need accurate timing.

 

Parameter Name

Description

TimerSpeed

(Optional) If 0.0 then all the object's timers will be suspended. If 1.0 then any suspended timers will be resumed. If not specified then it will suspend the object (with 0.0 value).

 

If between 0.0 and 1.0, the timers will be activated, but at a slower pace. If above 1.0, the timers will be activated at an accelerated pace.

SuspendContains

(Optional) If TRUE this will suspend all the objects contained by this object, and whatever they contain. If FALSE then only this object will be suspended (or resumed). If not specified then only this object will be affected.

Return value description

None.

 

Overrides: Call only the highest priority method

Common to all objects

 

Property Definitions

No property definitions in the library.

 

 

 

Objects

No objects in the library.

 

Functions

Abs

Returns the absolute value of a number.

 

Returns the absolute value of a number.

 

Parameter Name

Description

Value

A number.

Return value description

Absolute value.

 

ACos

Returns the arc-cosine of a number.

 

Returns the arc-cosine of a number.

 

Parameter Name

Description

Value

A number.

Return value description

Arc-cosine, in randians.

 

ASin

Returns the arc-sine of a number.

 

Returns the arc-sine of a number.

 

Parameter Name

Description

Value

A number.

Return value description

Arc-sine, in randians.

 

ATan

Returns the arc-tangent of a number.

 

Returns the arc-tangent of a number.

 

Parameter Name

Description

Value

A number.

Return value description

Arc-tangent, in randians.

 

ATan2

Returns the arc-tangent of a number.

 

Returns the arc-tangent of a number.

 

Parameter Name

Description

Y

Y-coordinate of the number.

X

X-coordinate of the number.

Return value description

Arc-tangent, in randians.

 

Ceil

If this isn't an integer, return's the next highest integer.

 

If this isn't an integer, return's the next highest integer.

 

Example:

 

ceil (123) returns 123.

ceil (123.1) returns 124.

ceil (123.8) returns 124.

ceil (-1.1) returns -1.

 

Parameter Name

Description

Value

A number.

Return value description

Next highest integer.

 

ClassesEnum

Enumerates all the classes.

 

This enumerates all the classes as strings. You can pass a class string into ObjectNew(), for example.

 

Parameter Name

Description

Return value description

List of all the classes, as strings.

 

Cos

Returns the cosine.

 

Returns the cosine.

 

Parameter Name

Description

Value

Angle, in radians.

Return value description

Cosine of the angle.

 

CosH

Returns the hypberboloic cosine.

 

Returns the hypberboloic cosine.

 

Parameter Name

Description

Value

Angle, in radians.

Return value description

Hypberboloic cosine of the angle.

 

DeleteGroup

Deletes a group of objects.

 

This accepts a list of objects and deletes them all at once, including their contained objects.

 

Parameter Name

Description

ObjectList

List of objects to delete.

DeleteContained

If TRUE then even the object's contents are deleted. If FALSE then the object's contents are left behind.

Return value description

TRUE if success.

 

Exp

Raise the constant, e, to the specified power.

 

Raise the constant, e, to the specified power.

 

Parameter Name

Description

Value

Number.

Return value description

Raise the constant, e, to the specified power.

 

Floor

If this isn't an integer, return's the next lowest integer.

 

If this isn't an integer, return's the lowest highest integer.

 

Example:

 

floor (123) returns 123.

floor (123.1) returns 123.

floor (123.8) returns 123.

floor (-1.1) returns -2.

 

Parameter Name

Description

Value

Number.

Return value description

Next lowest integer.

 

GlobalEnum

Enumerates all the global variables.

 

This enumerates all the global variables. It returns a list with their names.

 

Parameter Name

Description

Return value description

List with the names of all the global variables. The names are strings.

 

GlobalGet

Gets the value of a global.

 

This gets the value of a global.

 

x = GlobalGet ("MyGlobal");

 

 is equivalent to

 

x = MyGlobal;

 

The only difference is that GlobalGet() lets you access globals created after compile time.

 

Parameter Name

Description

Global

The name of the global. This must be a string.

Return value description

The value of the global, or Undefined if the global does not exist.

 

GlobalGetSet

Changes the get/set code for accessing the global.

 

This adds code for getting or setting a global. Applications can use this to dynamically create code to intercept a global get and set. (If no code exists then the global is accessed directly.)

 

Example:

 

GlobalSet ("MyNewGlobal", 0);

GlobalGetSet ("MyNewGlobal", GetFunc, SetFunc);

 

After this is called, any attempts to change "MyNewGlobal" will be passed to SetFunc(), and any attemps to get the value will be passed to GetFunc().

 

Parameter Name

Description

Global

The name of the global. This must be a string.

GetCode

This is the code that will be run when the global is read from. It must either be the name of an existing function or method (with object).

 

If this is NULL the global will be accessed directly when it's read.

SetCode

This is the code that will be run when the global is written to. It must either be the name of an existing function or method (with object).

 

If this is NULL the global will be accessed directly when it's written.

Return value description

TRUE if the global get/set code is changed, FALSE or Undefined if the call fails.

 

GlobalQuery

Tests to see if a global variable exists.

 

This tests to see if a global variable exists.

 

Parameter Name

Description

Global

The name of the global. This must be a string.

Return value description

TRUE if the global variable exists, FALSE if it doesn't.

 

GlobalRemove

Removes a global variable from the list.

 

This removes a global variable from the list.

 

Parameter Name

Description

Global

The name of the global. This must be a string.

Return value description

TRUE if the global is removed, FALSE if it doesn't exist.

 

GlobalSet

Sets the value of a global.

 

This sets the value of a global.

 

GlobalSet ("MyGlobal", 54);

 

 is equivalent to

 

MyGlobal = 54;

 

The only difference is that GlobalSet() lets you access globals created after compile time.

 

Parameter Name

Description

Global

The name of the global. This must be a string.

 

If the global does NOT exist then it will be created.

Value

The value to set the global to.

Return value description

The value of the global.

 

If this is used to create a global, it will normally return the value of the global. However, if the global cannot be created it will return Undefined.

 

GUIDStringToObject

Converts a 32-character GUID string to an object.

 

This converts a 32-character GUID string (from ObjectToGUIDString()) to an object reference. The string will be converted even if the object does not exist.

 

Parameter Name

Description

String

GUID-string of the object. This must be exactly 32 characters long, consisting only of '0'...'9', and 'a'...'f' (or 'A'...'F').

Return value description

Object defined by the GUID. The object reference will be created even if the object doesn't really exist. If the string is not a valid GUID-string then Undefined will be returned.

 

IsBOOL

Tests to see if the value is boolean.

 

Returns TRUE if the value is a boolean, FALSE if it is any other type.

 

Parameter Name

Description

Value

Value of any type.

Return value description

Boolean value.

 

IsChar

Tests to see if the value is a character.

 

Returns TRUE if the value is a character, FALSE if it is any other type.

 

Parameter Name

Description

Value

Value of any type.

Return value description

Boolean value.

 

IsCharAlpha

Returns TRUE if the character is alphabetical.

 

Returns TRUE if the character is alphabetical, such as 'A'..'Z' and 'a'..'z', including non-roman.

 

Parameter Name

Description

Value

Value of any type.

Return value description

Boolean value.

 

IsCharAlphaNum

Returns TRUE if the character is alphabetical or numeritcal

 

Returns TRUE if the character is alphabetical or numberical, such as 'A'..'Z' and 'a'..'z', including non-roman, or '0'..'9'.

 

Parameter Name

Description

Value

Value of any type.

Return value description

Boolean value.

 

IsCharDigit

Returns TRUE if the character is a digit.

 

Returns TRUE if the character is a digit, '0' .. '9'.

 

Parameter Name

Description

Value

Value of any type.

Return value description

Boolean value.

 

IsCharLower

Returns TRUE if the character is lower case.

 

Returns TRUE if the character is lower case.

 

Parameter Name

Description

Value

Value of any type.

Return value description

Boolean value.

 

IsCharSpace

Returns TRUE if the character is whitespace.

 

Returns TRUE if the character is whitespace, such as space, tab, and newline.

 

Parameter Name

Description

Value

Character.

Return value description

Boolean value.

 

IsCharUpper

Returns TRUE if the character is upper case.

 

Returns TRUE if the character is upper case.

 

Parameter Name

Description

Value

Character.

Return value description

Boolean value.

 

IsFunction

Tests to see if the value is a function.

 

Returns TRUE if the value is a function or an method associated with an object, FALSE if it is any other type.

 

Parameter Name

Description

Value

Value of any type.

Return value description

Boolean value.

 

IsInfinite

Tests to see if a number is infinite.

 

Returns TRUE if the number is infinite, FALSE if it's finite.

 

Parameter Name

Description

Value

Value of any type.

Return value description

Returns TRUE if the number is infinite, FALSE if it's finite.

 

IsList

Tests to see if the value is an list.

 

Returns TRUE if the value is an list, FALSE if it is any other type.

 

Parameter Name

Description

Value

Value of any type.

Return value description

Boolean value.

 

IsMethod

Tests to see if the value is a method.

 

Returns TRUE if the value is a method, FALSE if it is any other type.

 

Parameter Name

Description

Value

Value of any type.

Return value description

Boolean value.

 

IsNan

Tests to see if a value is a number.

 

Returns TRUE if the value is not-a-number (in the floating point sense), FALSE if it is a number (in the floating point sense).

 

Parameter Name

Description

Value

A number.

Return value description

Returns TRUE if the value is not-a-number (in the floating point sense), FALSE if it is a number (in the floating point sense).

 

IsNumber

Tests to see if the value is a number.

 

Returns TRUE if the value is a number, FALSE if it is any other type.

 

Parameter Name

Description

Value

Value of any type.

Return value description

Boolean value.

 

IsObject

Tests to see if the value is an object.

 

Returns TRUE if the value is an object, FALSE if it is any other type.

 

Parameter Name

Description

Value

Value of any type.

Return value description

Boolean value.

 

IsResource

Tests to see if the value is a resource.

 

Returns TRUE if the value is a resource, FALSE if it is any other type.

 

Parameter Name

Description

Value

Value of any type.

Return value description

Boolean value.

 

IsString

Tests to see if the value is a string.

 

Returns TRUE if the value is a string or string table entry, FALSE if it is any other type.

 

Parameter Name

Description

Value

Value of any type.

Return value description

Boolean value.

 

LanguageGet

Returns the current language.

 

Returns the language currently in use by the virtual machine. The current language affects what resources and string table entries are used; since they're language specific.

 

Parameter Name

Description

Return value description

Current language. This is a standard Windows language identifier number.

 

LanguageSet

Sets the current language.

 

This sets the current language.

 

Changing the language affects what resources and string table entries are used; since they're language specific.

 

Parameter Name

Description

Language

New language to use. This is a standard Windows language number.

Return value description

None

 

Log

Returns the natural log of the number.

 

Returns the natural log of the number.

 

Parameter Name

Description

Value

A number.

Return value description

Returns the natural log of the number.

 

Log10

Returns the log (base 10) of the number.

 

Returns the log (base 10) of the number.

 

Parameter Name

Description

Value

A number.

Return value description

Returns the log (base 10) of the number.

 

Max

Returns the higher of two values.

 

Returns the higher of two values.

 

Parameter Name

Description

Value1

A number or string.

Value2

A number or string.

Return value description

Returns the higher of two values.                                         

 

Min

Returns the lower of two values.

 

Returns the lower of two values.

 

Parameter Name

Description

Value1

A number or string.

Value2

A number or string.

Return value description

Returns the higher of two values.                                         

 

MMLFromList

Does the inverse of MMLToList().

 

This does the inverse of MMLToList(). It takes a list, in the same format as is output by MMLToList(), and returns a string with the MML/XML.

 

Parameter Name

Description

List

List, in the same output format is MMLToList().

OneMainTag

If TRUE, this assumes there is one main MML/XML tag for the entire string. If FALSE, it allows for many.

Return value description

String.                                                                                         

 

MMLToList

Converts a MML string (or resource) into a list of entries.

 

A MML string, like "<hello val=43>This is a test</hello>" is basically an XML string. Resource are also stored in a matter very similar to XML.

 

This function parses the string or resource, and returns a list with the MML (like XML) all parsed up.

 

The list contains three elements, [Name, Attributes, Contents].

 

Name is a string name of the tag, such as "hello".

 

Attributes is a list containing the attributes of the tag, or NULL if there are no attributes. Each attribute is a sub-list with [AttribName, AttribValue].

 

Contents is a list containing the contents of the tag, or NULL if there are no contents. The contents are a sub-list with strings, or more sub-lists, are are embeded MML/XML tags.

 

For example: "<hello val=43>This is a test</hello>" would be converted to ["hello", [ ["val", "43"] ] , ["This is a test"] ].

 

Parameter Name

Description

MMLOrResource

Either an MML string or a resource.

OneMainTag

If TRUE, this assumes there is one main MML/XML tag for the entire string. If FALSE, it allows for many. If a resource is passed in, this MUST be set to TRUE.

 

For example: "<hello>This is a test</hello>" would get converted to ["hello", NULL, ["This is a test"]] if OneMainTag is TRUE. It would be converted to [NULL, NULL, [ ["hello", NULL, ["This is a test"]] ]]  if OneMainTag is FALSE.

 

For example: "<p>LineOne</p><p>Line two</p>" MUST have OneMainTag set to TRUE.

Return value description

List. See the main description.                                               

 

ObjectClone

Clones an object or set of object.

 

This clones an object or set of objects. All the object's contents (sub-objects) are also cloned.

 

The object's properties and timers are copies, with references to cloned objects remapped to their clones. If an object is cloned, but its parent is NOT cloned, then the clone's will NOT be contained by any object.

 

Once all the objects have been cloned, the Property will be set to PropertyValue, and then all the objects will be called with Constructor2().

 

Parameter Name

Description

Clone

This is either a single object, or a list of objects. Any objects contained within these objects are also cloned.

Property

If this is a string, then all the cloned objects will have this propery, such as "pCloneOf" set to the value from PropertyValue. The value will be set BEFORE Constructor2() is called for the cloned object. This is useful to identify which objects are cloned.

 

If NULL, then no property will be set.

PropertyValue

Value to set the Property to.

Return value description

Returns a list of all the cloned objects. Each cloned object is a sub-list with [Original object, New object]. The list is NOT sorted. You can use this to later delete the clones.                                                  

 

ObjectEnum

Enumerates all the objects.

 

This returns a list of all the objects.

 

Parameter Name

Description

Return value description

List of all the objects.                         

 

ObjectNew

Creates a new object from a name or other object.

 

This creates a new object from a name or other object. It acts like "new XXX;" except that an arbitrary object class can be created.

 

Parameter Name

Description

Create

This can be a string containing the object's class, such as ObjectNew ("oMyObject"); which is equivalent to "new oMyObject;". Or, this can be another object, in which case the base class/layer will be used. (Ex: ObjectNew (oMyObject) will create an object with the same class as oMyObject.)

Return value description

The new object, or NULL if error.   

 

ObjectQuery

Tests to see if an object exists.

 

This tests to see if an object exists. An object may have been delete.

 

For example:

 

x = new Object;

ObjectQuery(x); // returns TRUE

delete x;

ObjectQuery(x); // returns FALSE

 

Parameter Name

Description

Object

Object to test.

Return value description

TRUE if the object exists, FALSE if it doesn't.

 

ObjectToGUIDString

Converts an object to a 32-character GUID string.

 

This converts an object reference to a 32-character GUID string. The string is 32 hexadecimal digits that uniquely identify the object.

 

Parameter Name

Description

Object

Object

Return value description

String with the object's GUID. If it's not an object then Undefined is returned.

 

Pow

Raise a number to the specified power.

 

Raise a number to the specified power.

 

Parameter Name

Description

Base

Number to raise.

Exponent

Power to raise it to.

Return value description

The number raised to the specified power.

 

Random

Returns a random value.

 

This returns a random value.

 

If no parameters are passed, it's a random value between 0 and 32767.

 

If one parameter is passed in, and the parameter is a list, this returns one of the elements of the list.

 

If one parameter is passed in, and the parameter isn't a list, this returns a random number between 0 (inclusive) and Value1. (exclusive)

 

If two parameters are passed in, this returns a random number between Value1 (inclusive) and Value2 (exclusive).

 

Parameter Name

Description

Value1

(Optional) A number. If only one parameter is provided then this can be a list.

Value2

(Optional) A number.

Return value description

Random number or selection from the list.                        

 

 

RandomSeed

Seeds the random value.

 

This seeds the random value. You can set the seed to ensure that calls to Random() are follow the same pattern every time.

 

Parameter Name

Description

Seed

(Optional) Seed number, 0 to 4 billion. If no parameter is used then the seed will be based on the current time.

Return value description

None                                                     

 

ResourceEnum

Enumerates a list of all the resources in the project.

 

This enumerates a list of resources in the project.

 

Parameter Name

Description

Return value description

List of resources.

 

Each resource is represented by a sub-list with [ResourceName, ResourceType]. ResourceName is the name of the resource, that can be passed to ResourceGet(). ResourceType is the type of the resource, such as "TransPros".        

 

ResourceGet

Gets a resource given a string name.

 

Gets a resource given a string name.

 

For example: ResourceGet ("rMyResource") will return the resource, rMyResource. If the resource doesn't exist, it returns NULL.

 

Parameter Name

Description

ResourceString

String for the resource.

Return value description

Resource as a value.

 

Round

Rounds the value to the nearest integer.

 

Rounds the value to the nearest integer.

 

Example:

 

round (123) returns 123.

round (123.1) returns 124.

round (123.5) returns 124.

round (123.8) returns 124.

round (-1.1) returns -1.

 

Parameter Name

Description

Value

A number.

Return value description

Rounds the value to the nearest integer.

 

Sin

Returns the sine.

 

Returns the sine.

 

Parameter Name

Description

Value

Angle, in radians.

Return value description

Sine of the angle.

 

SinH

Returns the hypberboloic sine.

 

Returns the hypberboloic sine.

 

Parameter Name

Description

Value

Angle, in radians.

Return value description

Hypberboloic sine of the angle.

 

Sqrt

Returns the square root of a number.

 

Returns the square root of a number.

 

Parameter Name

Description

Value

A number.

Return value description

Returns the square root of a number.

 

Tan

Returns the tangent.

 

Returns the tangent.

 

Parameter Name

Description

Value

Angle, in radians.

Return value description

Tangent of the angle.

 

TanH

Returns the hypberboloic tangent.

 

Returns the hypberboloic tangent.

 

Parameter Name

Description

Value

Angle, in radians.

Return value description

Hypberboloic tangent of the angle.

 

TimeFromDateTime

Converts from a year, month, day, etc. to a time (in days) since January 1, 1601.

 

This converts from a year, month, day, etc. to a time (in days) since January 1, 2001.

 

Parameter Name

Description

GMT

If TRUE then the time is GMT (system) time. If FALSE then time is in the local computer's time.

Year

Year. Example: 2004

Month

Month. 1..12

Day

Day. 1..31

Hour

                            Hour. 0..23

Minute

Minute. 0..59

Second

Second. 0..59

Milliseconds

Milliseconds. 0..999

Return value description

Time. If an error occurs this returns NULL.

 

TimeGet

Gets the time.

 

This returns the time. This is a single number, which is the number of days since January 1, 2001.

 

To convert it to year, month, day, etc. call TimeToDateTime()

 

Parameter Name

Description

Return value description

Number of days since January 1, 1601.

 

TimeSinceStart

Returns the number of seconds that the computer has been running.

 

Returns the number of seconds that the computer has been running.

 

This is a good time function to check how many seconds, minutes, or hours of real-time have ellapsed.

 

Parameter Name

Description

Return value description

Number of seconds since the computer was started.

 

TimeToDateTime

Converts from a time (in days) to a year, month, day, etc.

 

This converts from a time (in days) to a year, month, day, etc.

 

It accepts a time from TimeGet() or TimeFromDateTime() and returns a list. The elements of the list contain the year, month, day, etc.

 

Parameter Name

Description

Time

Time from TimeGet() or TimeFromDateTime()

GMT

If TRUE then the time is calculated as GMT (system) time. If FALSE then time is calculated in the local computer's time.

Return value description

Returns a list. List[0] = year (ex: 2004), List[1] = month (1..12), List[2] = day (1..31), List[3] = hour (0..23), List[4] = minute (0..59), List[5] = second (0..59), List[6] = milliseconds (0.999), List[7] = day of week (1..7).

 

TimeZone

Returns the time zone information.

 

This returns the offset in time for the current time zone, in days. UTC = local time + TimeZone().

 

Parameter Name

Description

Return value description

Offset in days.

 

ToBool

Converts the value to a boolean.

 

Converts the value to a boolean.

 

Parameter Name

Description

Value

Value of any type.

Return value description

Boolean value.

 

 

ToChar

Converts the value to a character.

 

Converts the value to a character.

 

Parameter Name

Description

Value

Value of any type.

Return value description

Character.

 

ToFunction

Converts the value to a function.

 

Converts the value to a function.

 

Parameter Name

Description

Value

Value of any type, but usually a string of the function's name.

Return value description

Function, or undefined if it can't be converted.

 

ToMethod

Converts the value to a public method.

 

Converts the value to a public method.

 

Parameter Name

Description

Value

Value of any type, but usually a string of the method's name.

Return value description

Method, or undefined if it can't be converted.

 

ToNumber

Converts the value to a number.

 

Converts the value to a number.

 

Parameter Name

Description

Value

Value of any type.

Return value description

Number.

 

 

ToObject

Converts an object.method to an object, or a hexadecimal string to an object.

 

Converts an object.method to an object, or a hexadecimal string to an object.

 

If the value is an Object.Method, this will return the object part. If the string is a 32-digit hexadecimal string, this will return an object ID, which may not be valid. If an object is passed in, the same object will be returned. Otherwise, it returns undefined.

 

Parameter Name

Description

Value

Value of any type.

Return value description

Object or undefined.

 

ToString

Converts the value to a string.

 

Converts the value to a string.

 

Parameter Name

Description

Value

Value of any type.

Return value description

String.

 

ToStringMML

Sanitizes a string to it can be placed into a resource.

 

Resources uses a tagged text system called MML (very similar to XML). MML uses special characters, like < and > to indicate the start of a section, such as:

 

<speak>Hello there!</speak>

 

Because some characters are used by the tag system (< and >), these can't be included directly in the strings. These need to be converted to character codes. In this case, '>' is converted to '&lt;' and '<' is converted to '&gt;'.

 

If you want to create create your own resources that include strings you will need to do these conversions. The easiest way is to use the ToStringMML() function.

 

For example: To speak "2 <3" you would use:

 

"<speak>" + ToStringMML("2 < 3") + "</speak";

 

This produces the string:

 

"<speak>2 &lt; 3</speak>"

 

Parameter Name

Description

String

The string that is to be "sanitizied" so it can be placed in a resource string.

Return value description

Sanitized version of the string, which characters like '<' converted to '&lt;', etc.

 

 

Trace

Outputs the string to the debug trace and/or for logging.

 

Calling trace with a string (or other value) will output the string to the debug trace. It will also output the string for logging purposes.

 

For example:

Trace ("Write this in the log.\n");

 

will write the data in the log.

 

Parameter Name

Description

String

String that should be output to the debug trace, or the log.

Return value description

None

 

TypeOf

Returns the data's type as a string.

 

Returns the data's type as a string.

 

The following strings are returned:

 

"bool" for a boolean

"char" for a character

"function" for a function

"list" for a list

"list.method" for a method call into a list

"method" for a method

"number" for a number

"null" for a NULL value

"object" for an object

"object.method" for a method call into an object

"resource" for a resource reference

"string" for a string

"string.method" for a method call into a string

"stringtable" for a stringtable reference

"undefined" for undefined

 

Parameter Name

Description

Value

Value to check the type of.

Return value description

String for the value's type.

 

 

Variables (Global)

NewLine

Shortcut to add a line within a string.

 

Shortcut to add a line within a string.

"\r\n"

Pi

3.141592653589793238

 

3.141592653589793238

3.141592653589793238

TwoPi

6.283185307179586476

 

6.283185307179586476

6.283185307179586476

 

 

Library – Server Library

Overview

This library is needed to use ther IF server's features.

 

The server library is necessary for the MIFL scripting language to communicate with the "MIF Server". It provides functionality for managing user connections over the internet. It accepts new user connections, sends and receives messages to users, etc.

 

List of Strings

 

List of Resources

rHelpCopyright

Copyright information.

<p><bold><big>

    Copyright information

</big></bold></p>

 

<p>

    Circumreality is copyright 2001-2009 by Mike Rozak, all rights reserved.

    See <bold>http://www.mXac.com.au</bold> for

    more information.

</p>

 

<p>

    The lexicon for text-to-speech comes from CMU University.

</p>

rTitleInfo

You must have one (and only one) rTitleInfo for every interactive fiction title. This is a placeholder resource that can be overridden.

rVoiceChatInfoDefault

Default voice-chat features that a player can use. You may wish to customize this for your own need, perhaps even on a character-race basis.

 

Method Definitions

ConnectionEnd

Called just before the connection object is destroyed..

 

ConnectionEnd() is called after just before a cConnection object is destroyed. You should replace this method with whatever functionality a connection should undertake when its shutting down.

 

Parameter Name

Description

Return value description

None

 

Overrides: Call only the highest priority method

NOT Common to all objects

ConnectionError

Called with an error occurs in the connection.

 

This method is called when an error occurs with the connection, basically meaning that a disconnect is necessary because the connection has terminated.

 

The default behavior of the ConnectionError() call for the Connection object is to delete itself.

 

Parameter Name

Description

ErrorNumber

Error number.

ErrorString

String describing the error.

Return value description

None

 

Overrides: Call only the highest priority method

NOT Common to all objects

ConnectionInfoForServer

Called when miscellaneous pieces of information are sent to the server.

 

Called when miscellaneous pieces of information are sent to the server. This includes messages for the user's time zone, graphics speed, and language.

 

Parameter Name

Description

Name

This is the value name. Currently, it can be:

 

"artstyle" - A number from 0 to 3 for the art style to use. See gVisualPainterlyNames.

 

"timezone" - The current time zone. Add this value (in hours) to GMT to create the local time.

 

"langid" - The current Windows language ID. "1033" is American English, for example.

 

"graphspeed" - The graphics speed setting that the user has chosen, from 0 to 4.

Value

A string value.

Return value description

None

 

Overrides: Call only the highest priority method

NOT Common to all objects

ConnectionLogOff

Called with a logoff message is received from the client.

 

Called with a logoff message is received from the client. It means that the client has shut down.

 

The default behavior of the ConnectionLogOff() is to set a timer for 30 (or whatever) seconds that will then cause a shutdown.

 

Parameter Name

Description

TimeToShutdown

If this is a number, then it's the number of seconds before the connection will shut down.

 

If this is 0 then the shutdown is aborted.

 

If Undefined or NULL, then the connection will shut down in 30 seconds, or whatever gConnectionLogOffTime is.

Return value description

None

 

Overrides: Call only the highest priority method

NOT Common to all objects

ConnectionMessage

Called with a new command message comes in from the client.

 

This method is called by the server when a new message comes in from the client. It is up to the client to process the message.

 

You should override this to handle the incoming message. Usually this involves forwarding the message to the player or character object.

 

Parameter Name

Description

LanguageID

The language of the message. This number is a standard LANGID from WindowNT. It can be passed into LanguageSet().

Message

A message string for the incoming message.

Return value description

None

 

Overrides: Call only the highest priority method

NOT Common to all objects

ConnectionSend

Sends a message to the user.

 

Sends a message to the user.

 

This also sends the message to any users that are spying on the current user.

 

Parameter Name

Description

MessageQueue

Pass true to queue this message up in the audio queue, so that if it's a message for more audio or graphics, it won't be heard/shown until it's turn in the queue.

 

If false then play this audio (or show the image) right away.

Message

Message to send. This is either a resource, or string that's wrapped up in MML tags to look like a resource.

Return value description

True if the message was sent, false if it failed.

 

Overrides: Call only the highest priority method

NOT Common to all objects

 

ConnectionSendVoiceChat

Sends a voice chat message to the user.

 

Sends a voice chat message to the user.

 

This also sends the message to any users that are spying on the current user.

 

Parameter Name

Description

MMLString

MML string to send, from MMLSpeakObjectVoiceChat().

VoiceChatBinary

Binary data of voice chat, from the connection's ConnectionVoiceChat() method.

Garble

If TRUE then garble the speech to simulate the characters not understanding one another.

Effect

0 for no effect. 1 for whisper.

Return value description

True if the message was sent, false if it failed.

 

Overrides: Call only the highest priority method

NOT Common to all objects

ConnectionStart

Called once the connection has been initialized.

 

ConnectionStart() is called after a new cConnection object has been created. You should replace this method with whatever functionality a new connection should undertake.

 

Parameter Name

Description

Return value description

None

 

Overrides: Call only the highest priority method

NOT Common to all objects

ConnectionUploadImage

Called with an image uploaded from the client.

 

This method is called by the server when a new image comes in from the client. It is up to the client to process the message.

 

You should override this to handle the incoming message. Usually this involves saving the image away and alerting the player.

 

Parameter Name

Description

Number

Image number, 1 based.

Width

Width of the image, in pixels.

Height

Height of the image in pixels.

Binary

JPEG binary for the image that can be saved to the database.

Return value description

None

 

Overrides: Call only the highest priority method

NOT Common to all objects

ConnectionVoiceChat

Called with voice chat data that comes from the client.

 

This method is called by the server when a voice chat packet comes in from one of the clients.

 

You should override this to handle the incoming message. Usually this involves sending the voice chat information to all players in the room.

 

Parameter Name

Description

SpeakTo

Object that the player is speaking to, or NULL if none.

SpeakingStyle

Speaking style. "speak" if normal speech. "whisper" is whispering. "shout" if yelling.

Language

Language string (localized to whatever the current language of the player is). You should make sure other players can understand the languages.

Binary

Compressed voice chat binary. This can be sent to the other players to hear and/or saved away.

Return value description

None

 

Overrides: Call only the highest priority method

NOT Common to all objects

 

Property Definitions

pConnection

Internet connection for the object.

 

If an object (such as an actor) is controlled by a real person, then the object's pConnection property should point to the connection object. Likewise, the connection object will have a pConnectionActor that points to the actor.

 

A connection can have only one actor, and only one actor should point to a connection.

pConnectionActor

Actor object that the connection controls.

 

This is the actor object (player character) that the connection controls.

pConnectionID

Number that identifies the connection.

 

This number identifies what connection the "Connection" object is assoated with.

pConnectionLanguage

Language ID used for the connection.

 

This is the language ID used for the connection. Pass this to LanguageSet().

pConnectionLogOffTime

Stores the number of seconds before the connection will log off.

 

Stores the number of seconds before the connection will log off.

 

This is linked to the "logoff" timer in the connection object. If the connection is working on logoff then the "logoff" timer will be set at pConnectionLogOffTime will be > 0. Thus, if you set pConnectionLogOffTime to 0, then make sure to kill the "logoff" timer.

pConnectionSpies

List of spy connections.

 

If an administrator is spying on another character, then this is a list of connections that are spying on this connections. When ConnectionSend() or ConnectionSendVoiceChat() are called, all the spies receive the messages too.

pConnectionTimeZone

Time zone offset.

 

This is an offset to add to local time to generate UTC, in days.

 

It's has an equivalent return to the TimeZone() call, which returns the offset in time for the current time zone, in days. UTC = local time + TimeZone().

pConnectionUser

User that controls this connection. (Points to cUser class)

 

User that controls this connection. (Points to cUser class)

pUserLogAlert

The alert level for the user, affecting what priority messages are logged.

 

This value is saved for each user, and stored in the connection object when the user logs on.

 

Most users have a value of 0 for pUserLogAlert. However, if you're concerned that the user may be cheating, set this to 1 or 2. Higher values will cause more events for the user to be logged.

 

Normally, only priority 1 and 2 events are logged, while 3 and 4 events are ignored. If pUserLogAlert is 1 then priorities 1 to 3 will be logged. If pUserLogAlert is 2 then priorities 1 to 4 will be logged.

 

Objects

cConnection

Object assocated with a server connection.

 

This object is automatically created when a new connection is made to the server. It then receives the ConnectionMessage() and ConnectionError() callbacks when information has been received from the user's machine (over the internet).

 

You may wish to override some of the methods of the connection object (particularly the ConnectionMessage() callback) to work with your software.

 

Object info

Description

Automatically creates as an object

 

Contained In

 

Super-classes

 

Properties

pConnectionID – undefined

pConnectionActor – NULL

pConnectionLanguage – 1033

pConectionUser – NULL

pConnectionSpies

pUserLogAlert

pConnectionLogOffTime

pConnectionTimeZone

 

ConnectionEnd

this wont compile; // so don't compile without a connectionEnd() call

 

// if this got called then you haven't written your own

// ConnectionEnd code. You can do so by creating an overlaid

// cConnection object in your own library, and supply only

// the ConnectionEnd() method

ConnectionError

TextLogPriorityAdjust (this);

 

if (gTextLogUser[1])

    TextLog ("ConnectionError (" + ErrorNumber + ", " + ErrorString + ")");

           // BUGFIX - More detailed connection error info

   

TextLogPriorityAdjust (NULL);

 

// start the log-off process

// BUGFIX - because this is the result of an error, log off immediately

ConnectionLogOff (0.00001);

 

ConnectionInfoForServer

this wont compile; // so don't compile without a connectioninfoforserver call

 

// if this got called then you haven't written your own

// ConnectionInfoForServer code. You can do so by creating an overlaid

// cConnection object in your own library, and supply only

// the ConnectionInfoForServer() method

ConnectionLogOff

// if it's undefined then calling from external callback

if (!IsNumber(TimeToShutdown))

    TextLogPriorityAdjust (this);

 

// if time to shut down is 0 then aborting

if (TimeToShutDown === 0) {

    if (pConnectionLogOffTime) {

           TimerRemove ("logoff");    // kill the timer

           pConnectionLogOffTime = Undefined;

    }

   

    if (!IsNumber(TimeToShutdown))

           TextLogPriorityAdjust (NULL);

    return;

}

 

// if there's already a timer then dont do anything

if (pConnectionLogOffTime) {

    if (!IsNumber(TimeToShutdown))

           TextLogPriorityAdjust (NULL);

    return;

}

 

// else, create timer

// BUGFIX - If only a temporary character then can log-off right away

// Do this so that queries to the server don't stick around for 30 seconds

if (IsNumber(TimeToShutDown))

    this.pConnectionLogOffTime = TimeToShutDown;

else if (!pConnectionActor || pConnectionActor.pIsTemporary)

    this.pConnectionLogOffTime = 0.1;

else

    this.pConnectionLogOffTime = gConnectionLogOffTime;

TimerAdd ("logoff", FALSE, pConnectionLogOffTime, this.ConnectionLogOffSuccess);

 

if (!IsNumber(TimeToShutdown))

    TextLogPriorityAdjust (NULL);

ConnectionMessage

this wont compile; // so don't compile without a connectionmessage call

 

// if this got called then you haven't written your own

// ConnectionMessage code. You can do so by creating an overlaid

// cConnection object in your own library, and supply only

// the ConnectionMessage() method

ConnectionSend

// send to spys

var i;

if (pConnectionSpies) for (i = 0; i < pConnectionSpies.ListNumber(); i++) {

    // if the spy has disconnected then remove from the list

    if (!ObjectQuery(pConnectionSpies[i])) {

           pConnectionSpies.ListRemove (i);

           i--;

           continue;

    }

   

    // send

    ConnectionSendFunc (pConnectionSpies[i].pConnectionID, MessageQueue, Message);

} // if spies

 

var vRet = ConnectionSendFunc (pConnectionID, MessageQueue, Message);

 

return vRet;

ConnectionSendVoiceChat

// send to spys

var i;

if (pConnectionSpies) for (i = 0; i < pConnectionSpies.ListNumber(); i++) {

    // if the spy has disconnected then remove from the list

    if (!ObjectQuery(pConnectionSpies[i])) {

           pConnectionSpies.ListRemove (i);

           i--;

           continue;

    }

   

    // send

    ConnectionSendVoiceChatFunc (pConnectionSpies[i].pConnectionID, MMLString, VoiceChatBinary, Garble, Effect);

} // if spies

 

 

var vRet = ConnectionSendVoiceChatFunc (pConnectionID, MMLString, VoiceChatBinary, Garble, Effect);

 

return vRet;

ConnectionStart

this wont compile; // so don't compile without a connection start call

 

// if this got called then you haven't written your own

// ConnectionStart code. You can do so by creating an overlaid

// cConnection object in your own library, and supply only

// the ConnectionStart() method

ConnectionUploadImage

this wont compile; // so don't compile without a connectionuploadimage call

 

// if this got called then you haven't written your own

// ConnectionUploadImage code. You can do so by creating an overlaid

// cConnection object in your own library, and supply only

// the ConnectionUploadImage() method

ConnectionVoiceChat

this wont compile; // so don't compile without a connectionvoicechat call

 

// if this got called then you haven't written your own

// ConnectionVoiceChat code. You can do so by creating an overlaid

// cConnection object in your own library, and supply only

// the ConnectionVoiceChat() method

Constructor

// place in the list, keeping it sorted

GConnectionList.ListInsert (this, GConnectionList.ListSearchToInsert(this));

Destructor

// just in case, send logoff

ConnectionSendFunc (pConnectionID, TRUE, "<logoff/>");

 

// call into the callback

ConnectionEnd();

 

// disconnect this connection in the server

ConnectionDisconnect (pConnectionID);

 

// remove this from the connection list

var index = GConnectionList.ListSearch (this);

if (index != -1)

    GConnectionList.ListRemove (index);

   

// if this is a single-player mode then shutdown the server

// after all the connections are gone

if (!GConnectionList.ListNumber() && ShardParamIsOffline())

    ShutDownImmediately(FALSE);

ConnectionLogOffSuccess (Private)

Called when the connection is actually allowed to log off.

 

This is called when the connection is actually allowed to log off, about 30 seconds after ConnectionLogOff() was called.

 

 

 

Parameter Name

Description

Return value description

 

 

TextLogPriorityAdjust (this);

 

if (gTextLogUser[1])

    TextLog ("ConnectionLogOff");

   

// Delete this connection

delete this;

 

TextLogPriorityAdjust (NULL);

oServerTimers

Maintains some timers useful for the server library.

 

Maintains some timers useful for the server library.

 

The timers maintain gPerformanceCPU and gPerformanceNetwork. They also automatically shut the server down and restart if the memory used by the server is > gMemoryMaximum.

 

Object info

Description

Automatically creates as an object

Yes

Contained In

 

Super-classes

 

Properties

 

 

 

Constructor

// disable logging if we're offline

if (ShardParamIsOffline()) {

    TextLogEnableSet (FALSE);

   

    // don't allow anything to be cached

    RenderCacheLimits (0, 0, 0, 0, 0);

}

else {

    RenderCacheLimits (gRenderCacheLimitsDataMaximum, gRenderCacheLimitsDataMinimumHardDrive,

           gRenderCacheLimitsDataGreedy, gRenderCacheLimitsDataMaxEntriesOn32,

           gRenderCacheLimitsDataMaxEntriesOn64);

}

 

// log startup

if (gTextLogSystem[1])

    TextLog ("STARTUP");

   

TimerAdd ("10sec", TRUE, 10.0, this.TenSecondTimer);

 

// text log delete timer

TimerAdd ("TextLogDelete", TRUE, 60 * 10 /* 10 min */ + 0.4351, this.TextLogDeleteOld);

Constructor2

// disable logging if we're offline

if (ShardParamIsOffline()) {

    TextLogEnableSet (FALSE);

   

    // don't allow anything to be cached

    RenderCacheLimits (0, 0, 0, 0, 0);

}

else {

    RenderCacheLimits (gRenderCacheLimitsDataMaximum, gRenderCacheLimitsDataMinimumHardDrive,

           gRenderCacheLimitsDataGreedy, gRenderCacheLimitsDataMaxEntriesOn32,

           gRenderCacheLimitsDataMaxEntriesOn64);

}

 

// log startup

if (gTextLogSystem[1])

    TextLog ("STARTUP");

   

// create the timer, just to make sure

TimerAdd ("10sec", TRUE, 10.0, this.TenSecondTimer);

 

// text log delete timer

TimerAdd ("TextLogDelete", TRUE, 60 * 10 /* 10 min */ + 0.4351, this.TextLogDeleteOld);

Destructor

// log shutdown

if (gTextLogSystem[1])

    TextLog ("SHUTDOWN");

   

TextLogDeleteOld (Private)

Tries to delete old text logs.

 

This method randomly selects a text log from the enumerated list. If it's older than the gTextLogDeleteOld then it's deleted.

 

 

Parameter Name

Description

Return value description

 

 

var vEnum = TextLogEnum();

if (!IsList(vEnum))

    return;

 

// select one

vEnum = Random(vEnum);

 

var vOld = TimeGet() - gTextLogDeleteOld;

if (vEnum[1] < vOld)

    TextLogDelete (vEnum[0]);

TenSecondTimer (Private)

Timer that's run exactly every 10 seconds.

 

Timer that's run exactly every 10 seconds.

 

This updates gPerformanceNetwork, gPerfomanceCPU, and will reboot the server is gMemoryMaximum is reached.

 

Parameter Name

Description

Return value description

 

 

// CPU usage

var vCPU = PerformanceCPU();

while (gPerformanceCPU.ListNumber() > 100)

    gPerformanceCPU.ListRemove (0);

gPerformanceCPU.ListConcat (vCPU);

 

// network usage

var vNetwork = PerformanceNetwork();

vNetwork.ListInsert (vCPU[0]);  // get the time from there

while (gPerformanceNetwork.ListNumber() > 100)

    gPerformanceNetwork.ListRemove (0);

gPerformanceNetwork.ListConcat (vNetwork);

 

// how much memory

var vMem1 = PerformanceMemory (1);

var vMem3 = PerformanceMemory (3);

if ((vMem1 > gSafetyMaximumMemory) || (vMem3 > gSafetyMaximumMemory)) {

    Trace ("Called ShutDownImmediatelyCheckIn() because exceeded gMemoryMaximum.");

    ShutDownImmediatelyCheckIn (TRUE);

    return;

}

 

if (!ShardParamIsOffline()) {

    // if there isn't enough hard drive then shut down immediately

    vMem1 = PerformanceDisk();

    if (vMem1 < gSafetyMinimumDisk) {

           Trace ("Called ShutDownImmediatelyCheckIn() because exceeded gSafetyMinimumDisk.");

           ShutDownImmediatelyCheckIn (TRUE);

           return;

    }

   

    // if there are too many GUI objects shut down immediately

    vMem1 = PerformanceGUI();

    if (vMem1 > gSafetyMaximumGUI) {

           Trace ("Called ShutDownImmediatelyCheckIn() because exceeded gSafetyMaximumGUI.");

           ShutDownImmediatelyCheckIn (TRUE);

           return;

    }

   

    // if there are too many threads, shut down immediately

    vMem1 = PerformanceThreads();

    if (vMem1 > gSafetyMaximumThreads) {

           Trace ("Called ShutDownImmediatelyCheckIn() because exceeded gSafetyMaximumThreads.");

           ShutDownImmediatelyCheckIn (TRUE);

           return;

    }

   

    // if there are too many handles shut down immediately

    vMem1 = PerformanceHandles();

    if (vMem1 > gSafetyMaximumHandles) {

           Trace ("Called ShutDownImmediatelyCheckIn() because exceeded gSafetyMaximumHandles.");

           ShutDownImmediatelyCheckIn (TRUE);

           return;

    }

}

 

Functions

BinaryDataEnum

Enumerates all the files in the database.

 

Enumerates all the files in the database.

 

Parameter Name

Description

FileNamePrefix

If this is a string then only those files beginning with FileNamePrefix will be enumerated. If NULL then all files will be enumerated.

Return value description

If successful, a list of file names (in no particular order). If it fails then it returns NULL.

 

BinaryDataGetNum

Returns the name of a file in the binary database, based upon the index number.

 

Returns the name of a file in the binary database, based upon the index number.

 

Parameter Name

Description

Index

Index number, from 0 .. BinaryDataNum()-1.

Return value description

String for the file name, or NULL if error.

 

 

BinaryDataLoad

Loads a binary file from the database.

 

Loads a binary file from the database.

 

Parameter Name

Description

FileName

File name to load.

Return value description

If successful, this returns a string with the binary data. The values of the string are all 1 more than the original data. Therefore, String[N] = OriginalData[N]+1.

 

Returns NULL if error.

 

BinaryDataNum

Returns the number of files in the database.

 

Returns the number of files in the database.

 

Parameter Name

Description

Return value description

Returns the number of files in the database.

 

BinaryDataQuery

Returns information about the file in the binary database.

 

Returns information about the file in the binary database.

 

Parameter Name

Description

FileName

File name to load.

Return value description

If this fails, returns NULL.

 

If successful, returns a list with [FileSize, CreationDate, LastModify, LastAccess]. FileSize is the size of the file in bytes. CreationDate is the date/time when the file was created. LastModify is the last modification date/time. LastAccess is the last access date/time, although this value is unreliable since it isn't always written out.

 

BinaryDataRefresh

Sends a message to all connected clients saying that database file has changed.

 

Calling this function sends a message to all connected clients letting them know that the database file has changed and they should reaload it.

 

Parameter Name

Description

FileName

The file that has been changed. This will have recently been written to BinaryDataSave()

Return value description

None

 

BinARYdATArEMOVE

Deletes binary data from  the binary database.

 

Deletes binary data from  the binary database.

 

Parameter Name

Description

FileName

File name to delete

Return value description

TRUE if the file was found and deleted, FALSE if it could not be found.

 

 

BinaryDataRename

Renames a file in the binary database.

 

Renames a file in the binary database.

 

Parameter Name

Description

FileName

File name to rename.

RenameTo

                  Name to rename it to. If the file already exists then the rename call will fail.

Return value description

TRUE if the file was found and renamed, FALSE if it could not be found or if RenameTo already exists.

 

BinaryDataSave

Saves binary data into the binary database.

 

Saves binary data into the binary database.

 

Any user images, user sounds, etc. should be saved in this database so they can be accessed by all users.

 

NOTE: Even though data is written to the binary database, it will not automatically be refreshed on the players' machines. To enfore a refresh, you'll need to send out the BinaryDataRefresh() message to all the clients, as well as having them draw their current room's contents.

 

Parameter Name

Description

FileName

File name to save this as.      

Data

This is a string with the data in it. The actual binary data is Data[N] - 1.

Return value description

TRUE if the data was saved, FALSE if there was an error.

 

BUGBUG

Not really a function. Used to find bug comments.

 

If you put BUGBUG comments in your properties and function descriptions to indicate that work needs to be done, then whenever help is built, you can look at this BUGBUG "function" to see where those references are.

 

Parameter Name

Description

Return value description

TRUE if the data was saved, FALSE if there was an error.

 

 

ConnectionBan

This bans an IP address.

 

If a user from a specific IP address it constantly logging in and staying logged, you can use ConnectionBan() to ban the IP address outright so that MIFL isn't even notified of the connection. Basically, as soon as the connection occurs it's disconnected.

 

The banned IP address will stay banned until the server is restarted (once a day, or however often then server reboots).

 

Note: This banning is different than some of the other IP address banning/blacklisting functions because the connection never gets to MIFL.

 

Calling ConnectionBan() DOESN'T disconnect the IP address if it's already connected.

 

Parameter Name

Description

IPAddress

A string of the IP address to ban, such as "12345.67.89".

BanAmount

Each IP address keeps a "ban value" with it, starting at 0. Whenever ConnectionBan() is called, the ban value is increased by BanAmount. When BanAmount >= 1.0 the IP address is banned.

BanDays

Number of days to ban. This information will be forgotten as soon as the server shuts down.

Return value description

TRUE if the address was succefully banned.

 

ConnectionDisconnect

Has the server disconnect with one of the connections.

 

Calling ConnectionDisconnect() will cause the server to disconnect from the given connection number.

 

This is no usually called directly, but is automatically handled when the Connection object is deleted.

 

Parameter Name

Description

ConnectionIdent

Connection number to disconnect.

Return value description

Returns true if the connection was disconnected, false if the connection number wasn't valid.

 

ConnectionEnum

Enumerates all the connections to the server.

 

This enumerates all the connections made to the server.

 

Parameter Name

Description

Return value description

List of connection identifiers.

 

ConnectionInfoGet

Gets a piece of information about the connection.

 

This will get a piece of information about the connection.

 

The types of information are:

 

"ip" - The user's IP address.

 

"status" - The current status of the connection, such as "logging on".

 

"user" - Name of the user.

 

"uniqueid" - Uniquely identifies user's machine. Empty string if no ID.

 

"character" - Name of the user's character.

 

"object" - The object that will be used to send the ConnectionMessage() and ConnectionError() messages to.

 

"sendbytes" - Number of bytes that have been sent by the server to the user's machine. (This total is BEFORE the data compression.)

 

"sendbytescomp" - Like "sendbytes", except compressed data.

 

"sendbytesexpect" - Number of bytes expected to be send.

 

"receivebytes" - Number of bytes received by the server from the user's machine.

 

"receivebytescomp" - Like "receivebytes", except compressed data.

 

"receivebytesexpect" - Number of bytes that expect to receive.

 

"connecttime" - Number seconds that the user has been connected to the server.

 

"sendlast" - Number of seconds since data was last sent to the user.

 

"receivelast" - Number of seconds since data was last received from the user.

 

"bytespersec" - Average bytes per second received from the user.

 

Parameter Name

Description

ConnectionIdent

Connection number to get info about.

InfoString

String indicating the type of information to send, such as "ip".

Return value description

Returns the value requested, or Undefined if the InfoString was wrong.

 

ConnectionInfoSet

Sets a piece of information about the connection.

 

This will set a piece of information about the connection.

 

The types of information are:

 

"ip" - The user's IP address. This doesn't affect the real IP address used, but is useful for record keeping.

 

"status" - The current status of the connection, such as "logging on".

 

"user" - Name of the user.

 

"uniqueid" - Uniquely identifies user's machine. Empty string if no ID.

 

"character" - Name of the user's character.

 

"object" - The object that will be used to send the ConnectionMessage() and ConnectionError() messages to. Changing this will redirect the messages to a different object.

 

Parameter Name

Description

ConnectionIdent

Connection number to set info about.

InfoString

String indicating the type of information to send, such as "ip".

Value

New value to set.

Return value description

Returns true if the value was changed, false if it failed to be changed.

 

ConnectionNew

Called by the server when a new user connects.

 

This function is called by the server when a new user connects to the server. The function creates a "Connection" object and returns that object. Any further callbacks, such as ConnectionMessage() and ConnectionError() will be sent to the connection object.

 

You may override this with your own function.

 

Parameter Name

Description

ConnectionIdent

Number that uniquely identifies the connection. The created object will remember this connection number.

Return value description

Object that's associated with the connection. From now on, the connection object will be passed ConnectionMessage() and ConnectionError() calls when it receives an incoming message or an error in the connection.

 

If a non-object is returned then the new connection will be disconnected.

 

ConnectionQueuedToSend

Returns the number of bytes queued to be send out to a connection.

 

This returns the number of bytes queued up to be sent to a connection. It does not includes shared data, such as the dowload-on-demand files.

 

You can use this to ensure that a user isn't intetionally creating a backlog on their side and trying to crash the server.

 

Parameter Name

Description

ConnectionIdent

Connection number to get info about.

Return value description

Returns the number of bytes queued up to be sent to out to the connection. This does NOT include data from files to be uploaded to the client as requested.

 

ConnectionRenderCacheSend

Sends a cached render or TTS .wav to the player's computer.

 

This sends a cached render of TTS .wav file to the player's computer.

 

It's used by MMLSpeak() and MMLSpeakNarrator() to pre-send cached TTS to the player's computer so it speaks right away.

 

Parameter Name

Description

ConnectionIdent

Connection number to get info about.

MMLString

MML string that names the render/TTS.

Return value description

Returns the quality of the data sent, from 0+.

 

If this failed, returns -1.

 

ConnectionSendFunc

Sends a message to the user.

 

This sends a message to the user.

 

NOTE: Use the ConnectionSend() method instead of ConnectionSendFunc().

 

Parameter Name

Description

ConnectionIdent

Connection number to send to.

MessageQueue

Pass true to queue this message up in the audio queue, so that if it's a message for more audio or graphics, it won't be heard/shown until it's turn in the queue.

 

If false then play this audio (or show the image) right away.

Message

Message to send. This is either a resource, or string that's wrapped up in MML tags to look like a resource.

Return value description

True if the message was sent, false if it failed.

 

ConnectionSendVoiceChatFunc

Sends a voice chat message to the user.

 

This sends a voice chat message to the user.

 

NOTE: Use the ConnectionSendVoiceChat() method instead of ConnectionSendVoiceChatFunc().

 

 

Parameter Name

Description

ConnectionIdent

Connection number to send to.

MMLString

MML string to send, from MMLSpeakObjectVoiceChat().

VoiceChatBianry

Binary data of voice chat, from the connection's ConnectionVoiceChat() method.

Garble

If TRUE then garble the speech to simulate the characters not understanding one another.

Effect

0 for no effect. 1 for whisper.

Return value description

True if the message was sent, false if it failed.

 

ConnectionsLimit

Limit the number of connections available on the shard.

 

Call this function to limit the number of connections available on the shard.

 

This limit will also be shown to players wondering how full the world is.

 

This function is automatically called based on gSafetyMaximumUsersConnected.

 

Parameter Name

Description

Number

Limit, from 1 to (around) 2000.

Return value description

TRUE if changed. FALSE if the number is too high/low.

 

 

DatabaseBackup

Backs up the database.

 

You can use this function to make nightly backups of the database.

 

This first saves the database (essentially callying DatabaseSave()), and then copies all the database files to a backup directory.

 

Parameter Name

Description

Directory

Name of the directory where to backup to, such as "c:\\my backups\\monday".

Return value description

Returns TRUE if the database was backed up, FALSE if there was an error.

 

DatabaseObjectAdd

Adds a new object to the database.

 

This adds the given object to the database (so it will be backed up to disk). It immediately checks out the added object.

 

Once an object has been added you will need to call DatabaseObjectCheckIn() when it's finished being used, before it's deleted.

 

Parameter Name

Description

DatabaseName

Name of the database. This cannot contain any spaces, and must be made up of letters, numbers (except the fist character), and underscores.

Object

Object to be added to the database.

Return value description

Returns TRUE if the object was added, FALSE if it couldn't be added (perhaps because it already exists in the database).

 

DatabaseObjectCheckIn

Checks in an object to the database.

 

This checks in an object that was checked out. Every time DatabaseObjectCheckOut() is called to create an object, a call to DatabaseObjectCheckIn() must be called when the object is finished being used (just before it's deleted).

 

Calls to DatabaseObjectCheckIn() deletes the object from the virtual machine unless a parameter (DontDelete) is set.

 

The checked in object will (eventually) be saved to disk. To force a checked-in object to be saved right away, call DatabaseSave(); this can be a slow call since it will save everything in the database.

 

Parameter Name

Description

DatabaseName

Name of the database. This cannot contain any spaces, and must be made up of letters, numbers (except the fist character), and underscores.

Object

Object which is to be checked in.

DontSave

(Optional) If set to TRUE then the checked-in object won't be saved, which means any changes to it will be lost. If FALSE (or not specified) the object will be saved.

DontDelete

(Optional) If set to TRUE then the checked-in object won't be deleted after it's checked in. If FALSE (or not specified) then the object is automatically deleted from the virtual machine after it's saved to the database.

Return value description

Returns TRUE if the object was checked in, FALSE if it couldn't be checked in (perhaps because the object is not checked out).

 

DatabaseObjectCheckOut

Checks out an object from the database.

 

Call this function to load an object from the database into the current virtual machine. Once an object is checked out you will need to check it in before the object is destroyed.

 

For example: If a monster (aka: object) is in a seldom-visited room then keeping it saved to disk until a player enters the room will save memory on the server. When the player enters the room, the monster would be checked-out. (The object ID can be gotten by calling DatabaseObjectQuery() to find all checked-in objects within the room.)

 

Parameter Name

Description

DatabaseName

Name of the database. This cannot contain any spaces, and must be made up of letters, numbers (except the fist character), and underscores.

Object

Object which is to be checked out.

Return value description

Returns TRUE if the object was added, FALSE if it couldn't be checked out (perhaps because the object is already checked out).

 

DatabaseObjectDelete

Removes an object from the database.

 

This deletes the given object from the database.

 

An object cannot be deleted if it is checked out.

 

Parameter Name

Description

DatabaseName

Name of the database. This cannot contain any spaces, and must be made up of letters, numbers (except the fist character), and underscores.

Object

Object to be deleted from the database.

Return value description

Returns TRUE if the object was deleted, FALSE if it couldn't be deleted (perhaps because it doesn't exist or is checked out).

 

DatabaseObjectEnumCheckOut

Returns a list of objects checked out in the database.

 

Returns a list of objects checked out in the database.

 

Parameter Name

Description

DatabaseName

Name of the database. This cannot contain any spaces, and must be made up of letters, numbers (except the fist character), and underscores.

Return value description

List of all the objects in the database that are checked out to this VM.

 

DatabaseObjectGet

Gets the Nth (checked out) object in the datbase.

 

This gets the Nth (potentially checked-out) object in the database.

 

It can be used to see what objects are in the database and/or checked out, and if they should be checked out or deleted.

 

Parameter Name

Description

DatabaseName

Name of the database. This cannot contain any spaces, and must be made up of letters, numbers (except the fist character), and underscores.

Index

Index to get, from 0 to DatabaseObjectNum()-1.

CheckedOut

If TRUE then this gets the Nth checked out item in the database. If FALSE if gets the Nth item (checked out or checked in).

Return value description

Object, or NULL/FALSE if error.

 

DatabaseObjectNum

Returns the number of objects (checked out) from the database.

 

Returns the number of objects (potentially checked out) from the database. This function is useful for making the DatabaseObjectGet() call.

 

Parameter Name

Description

DatabaseName

Name of the database. This cannot contain any spaces, and must be made up of letters, numbers (except the fist character), and underscores.

CheckedOut

If TRUE returns the number of checked out objects. If FALSE returns the number of objects (total) in the database.

Return value description

Returns the number of checked-out objects, or FALSE if error.

 

DatabaseObjectQueryCheckOut

Checks to see if an object is checked out.

 

Checks to see if an object is checked out.

 

Parameter Name

Description

DatabaseName

Name of the database. This cannot contain any spaces, and must be made up of letters, numbers (except the fist character), and underscores.

Object

Object to be to see if it's checked out.

TestOtherVM

If TRUE then this will see if the object is checked out on another VM too (which is slower). If FALSE this only tests for the current VM.

Return value description

2 if the object is checked out to this VM. 1 if it's checked out to another VM (not yet an issue, since a database is only owned by on VM at the moment). 0 if it's not checked out. -1 if the object does not exist in the database.

 

DatabaseObjectQueryCheckOut2

Checks to see if an object is checked out to any database.

 

Checks to see if an object is checked out to any database.

 

Parameter Name

Description

Object

Object to be to see if it's checked out.

TestOtherVM

If TRUE then this will see if the object is checked out on another VM too (which is slower). If FALSE this only tests for the current VM.

Return value description

If the object is checked out, returns the database name as a string. If it isn't then returns a NULL.

 

DatabaseObjectSave

Saves a checked-out object to the database.

 

This saves a checked-out object to the database. An application should call this if any important information in the object changes so that in the event of an application crash, the object will be saved.

 

If an object is checked-in it will automatically be saved.

 

Once an object is saved to the database, it is normally written to disk at the application's convenience. To write to disk immediately (which can be slow), call DatabaseSave().

 

Parameter Name

Description

DatabaseName

Name of the database. This cannot contain any spaces, and must be made up of letters, numbers (except the fist character), and underscores.

Object

Checked-out object that is to be saved to disk.

Return value description

Returns TRUE if the object was added, FALSE if it couldn't be saved (perhaps because the object is not checked out).

 

DatabasePropertyAdd

Causes a property to be cached by the database.

 

The database automatically caches some commonly-used property values when an object is added, checked in, or saved. Cached properties can be accessed much more quickly than ones that aren't cached. Thus, (as a general rule) calls to DatabasePropertyGet() and DatabaseQuery() should only be passed properties that are cached, or they will become extremely slow.

 

To tell a database to cache a property, call DatabasePropertyAdd().

 

Make sure to call this BEFORE filling the database with  any objects since calling it after objects have been added will result in calls for DatabasePropertyGet() and DatabaseQuery() for the given property to return Undefined. (This will be rectified the next time the object is saved or checked in though.)

 

Parameter Name

Description

DatabaseName

Name of the database. This cannot contain any spaces, and must be made up of letters, numbers (except the fist character), and underscores.

Property

Name of the property.

Return value description

Returns TRUE if the property was added, FALSE if it couldn't be added.

 

DatabasePropertyEnum

Lists all of the cached properties.

 

This returnes a list of all the properties cached by the database.

 

Parameter Name

Description

DatabaseName

Name of the database. This cannot contain any spaces, and must be made up of letters, numbers (except the fist character), and underscores.

Return value description

Returns a list of properties cached by the database.

 

DatabasePropertyGet

Gets one or more properties from one or more objects.

 

This gets one or more properties from one or more objects stored in the database. If the properties are cached (See DatabasePropertyAdd()) then the values will be retrieved much quicker.

 

NOTE: If a property is not cached, and it's a default value for the object, then DatabasePropertyGet() will return Undefined for the value. (Example: If the object is a "cLantern",  the default "Weight" for a cLantern is 10, and this lantern's weight is 10, then DatabasePropertyGet() will return Undefined if the property is not cached.) This happens because when an object is saved any properties that are the same as the object's default aren't written out (to save disk space).

 

Parameter Name

Description

DatabaseName

Name of the database. This cannot contain any spaces, and must be made up of letters, numbers (except the fist character), and underscores.

Objects

This is either a single object, or a list containing one or more objects.

Properties

This is either a string representing a public property, or a list containing one or more strings.

 

A property can also be:

"containedin" - Returns the object this is contained in, or NULL if not contained.

"dataischeckedout" - To see if the object is checked out.

"datadatecreated" - To find the date that the object was created.

"datadatemodified" - To find the date that the object was last modified.

"datadateaccessed" - To find the date that the object was last checked out.

(Note: The dates are returns as the number of days since Jan 1, 2001.)

Return value description

Returns FALSE if there was an error getting the property.

 

If it succedes, the return is one of the following formats:

 

Single object and single property - This returns the value for the property.

 

Single object and list of properties - This returns a list containing values, corresponding to the properties. For example: If Properties had ["Name", "Age", "Height"] passed in, the return would be ["Mike", "20", "1.9m"]

 

List of objects and single property - The return is a list of the property value for each object. For example: If Objects had [Mike, Fred, George] passed in, with a Property of "Age", then the return would be [20, 43, 15].

 

List of objects and list of properties - This returns a list of lists. The top-level entries all correspond to the object. The secondard list corresponds to the properties. Thus, if Objects was [Mike, Fred, George] and Properties was ["Name", "Age", "Height"], then the return would be [["Mike", 20, "1.9m"], ["Fred", 43, "1.8m"], ["George", 15, "1.7m"]]

 

DatabasePropertyRemove

Removes a cached property from the database.

 

This removes a property that was cached by a call to DatabasePropertyAdd()

 

Parameter Name

Description

DatabaseName

Name of the database. This cannot contain any spaces, and must be made up of letters, numbers (except the fist character), and underscores.

Property

Name of the property.

Return value description

Returns TRUE if the property was removed, FALSE if it couldn't be removed.

 

DatabasePropertySet

Sets one or more properties from one or more objects.

 

This sets one or more properties for one or more objects stored in the database.

 

If an object is checked out then DatabasePropertySet() will fail.

 

Changed properties will (eventually) be saved to disk. To save them immediately, call DatabaseSave().

 

Parameter Name

Description

DatabaseName

Name of the database. This cannot contain any spaces, and must be made up of letters, numbers (except the fist character), and underscores.

Objects

This is either a single object, or a list containing one or more objects.

Properties

This is either a string representing a public property, or a list containing one or more strings.

 

A property can also be:

"containedin" - Returns the object this is contained in, or NULL if not contained.

"dataischeckedout" - To see if the object is checked out.

"datadatecreated" - To find the date that the object was created.

"datadatemodified" - To find the date that the object was last modified.

"datadateaccessed" - To find the date that the object was last checked out.

 

(Note: The dates are returns as the number of days since Jan 1, 2001.)

Values

The format of values depends upon whether a single object is passed in, or a list of objects, and whether a single property is used or a list of properties.

 

Single object and single property - This is the value for the property.

 

Single object and list of properties - This is a list containing the new values, corresponding to the properties. For example: If Properties had ["Name", "Age", "Height"] passed in, this could be ["Mike", "20", "1.9m"]

 

List of objects and single property - The parameter is a list of the property value for each object. For example: If Objects had [Mike, Fred, George] passed in, with a Property of "Age", then this parameter could be [20, 43, 15].

 

List of objects and list of properties - This must be a list of lists. The top-level entries all correspond to the object. The secondary list corresponds to the properties. Thus, if Objects was [Mike, Fred, George] and Properties was ["Name", "Age", "Height"], then this could be [["Mike", 20, "1.9m"], ["Fred", 43, "1.8m"], ["George", 15, "1.7m"]]

Return value description

Returns TRUE if success, FALSE if there's an error (such as one of the objects being checked out).

 

DatabaseQuery

Queries the database for objects meeting the given conditions.

 

This searches through the database for all objects meeting the given conditions. The objects are returned in a list.

 

NOTE: The conditions should rely on attributes that are cached in the database (See DatabasePropertyAdd()) since un-cached attributes will be slower.

 

NOTE: If a property is not cached, and it's a default value for the object, then DatabasePropertyGet() will return Undefined for the value. (Example: If the object is a "cLantern",  the default "Weight" for a cLantern is 10, and this lantern's weight is 10, then DatabasePropertyGet() will return Undefined if the property is not cached.) This happens because when an object is saved any properties that are the same as the object's default aren't written out (to save disk space).

 

Parameter Name

Description

DatabaseName

Name of the database. This cannot contain any spaces, and must be made up of letters, numbers (except the fist character), and underscores.

Conditions

This defines the condiitons that must be met for an object to be returned. (You could also think of them as a filter.) If this NULL then all objects are returned. Otherwise, it is a list with the following format:

 

The first element in the conditions list is an operator string indicating what kind of condition is requires. The next parameters depend upon the operators.

 

For example: {"<", "|Age", 32} will return all entries where the "Age" Property is less than 32. {"containsc", "|Name", "Mike"} will return all objects where the "Name" property contains the text "Mike".

 

The following operators are supported:

 

Single operand: 2nd item in the list is either an attribute string or another list.

"-" - Negation.

"~" - Bitwise not.

"!" - Loglical not.

 

Two operands:

"%" - Modulo.

"<<" - Bitwise left

">>" - Bitwise right

"<" - Less than

"<=" - Less than equals

">" - Greater than

">=" - Greater than equals

"==" - Equality

"!=" - Not equal

"===" - Strict equality

"!==" - Not equal, strict equality

"==c" - Case insentative equals

"!=c" - Case insentative not-equals

"contains" - See if the 2nd item's string is contained in the first item's string.

"containsc" - Case insensative contains.

 

Any number of operands: Any number of elements.

"*" - Multiplication.

"/" - Division

"+" - Addition

"-" - Subtraction

"&" - Bitwise and

"^" - BItwise xor

"|" - Bitwise or

"&&" - Logical and

"||" - Logical or

 

 

The operands (list values other than the first element) can be a value, a property name, or another list of conditions.

 

Property name - To indicate that a string is the name of a property, as opposed to a string, prefix the property with a '|' character (or whatever is passed into PropDisambig). For example "|Name" refers to the property "Name", while "Name" is just a string.

 

Sub-conditions - A condition can have sub-conditions. For example: To find all people whose name is "Mike" or "Fred", then pass in ["||", ["==c", "|Name", "Mike"], ["==c", "|Name", "Fred"]].

 

 

 

A property can also be:

"containedin" - Returns the object this is contained in, or NULL if not contained.

"dataischeckedout" - To see if the object is checked out.

"datadatecreated" - To find the date that the object was created.

"datadatemodified" - To find the date that the object was last modified.

"datadateaccessed" - To find the date that the object was last checked out.

(Note: The dates are returns as the number of days since Jan 1, 2001.)

PropDisambig

This is the character that is placed at the beginning of a string to indicate that the string represents a property. '|' is usually used, but any character is possible.

 

If 0 is passed in, then all strings are assumed to be properties.

Return value description

Returns FALSE if there was an error enumerating the objects.

 

If successful, it returns a list with all the objects (in the database) which pass the conditions.

 

DatabaseSave

Saves the database to disk.

 

Normally, the database is saved bit-by-bit to disk, so that users don't see multi-second pauses in game-play. However, this can be dangerous since if the server crashes, not all the data will be saved.

 

To ensure that all the data is saved, call DatabaseSave().

 

Parameter Name

Description

DatabaseName

(Optional) Name of the database. This cannot contain any spaces, and must be made up of letters, numbers (except the fist character), and underscores.

 

If no database is specified then all of them will be saved.

Return value description

Returns TRUE if the database was saved, FALSE if there was an error.

 

EmailSend

Sends E-mail from the sever.

 

Call this to send E-mail from the server.

 

It connects using SMTP to send the message.

 

Since this is an asynchronous function, and won't return success/failure, all the E-mail transmission and success information is automatically logged.

 

NOTE: This CAN'T send mail using SSL at the moment!

 

Parameter Name

Description

Domain

String of the domain that sending from, such as "bigpond.com".

SMTPServer

SMTP server, such as "mail.bigpond.com".

EmailTo

Email address of the person sending to, such as "Mike@mXac.com.au". This MUST be a valid E-mail address without extra spaces.

EmailFrom

Email address of the sender, such as "AutoSend@mXac.com.au". This MUST be a valid E-maill address without extra spaces.

NameFrom

String to display in the "from" field, such as "Interactive Fiction Automatic Mailer".

Subject

Subject string.

Message

Message text.

AuthUser

Some E-mail systems requires user authentication before they send E-mail. If your system need this, make sure to fill in AuthUser and AuthPassword. Otherwise, leave them as empty strings, "".

AuthPassword

Some E-mail systems requires user authentication before they send E-mail. If your system need this, make sure to fill in AuthUser and AuthPassword. Otherwise, leave them as empty strings, "".

Return value description

Returns TRUE, but this doesn't guarantee the mail has actually been sent since its an asnchronous process.

 

HelpArticle

Returns the resource (in a list) for a given online help article.

 

Returns the resource (in a list) for a given online help article. If the article does not exist then it returns NULL.

 

Parameter Name

Description

Actor

The actor looking in help. Some actors, either because of skills or access priviledges, won't be able to see some help topics.

ArticleName

String with the article's name. The name must be an exact match (except for case.)

Books

Controls what books of help will be searched.

 

If this is NULL, then only help articles in the default book, "", will be searched. (NULL will usually be passed in.)

 

If it's a string then only that book will be searched.

 

If it's a list of strings, then only those books whose string appears in the list will be kept.

Return value description

If the article cannot be found it returns NULL. Otherwise, it returns a list.

 

The first item is the MML text for the article, of the form "<Help>...</Help>".

 

The second item is the TOC directory, such as "major topic/minor topic". It might just be an empty string, like "". The third item the secondary TOC directory, if one exists.

 

HelpArticleMML

Returns the resource for a given online help article.

 

Returns the resource for a given online help article. If the article does not exist then it returns NULL.

 

Parameter Name

Description

Actor

The actor looking in help. Some actors, either because of skills or access priviledges, won't be able to see some help topics.

ArticleName

String with the article's name. The name must be an exact match (except for case.)

Books

Controls what books of help will be searched.

 

If this is NULL, then only help articles in the default book, "", will be searched. (NULL will usually be passed in.)

 

If it's a string then only that book will be searched.

 

If it's a list of strings, then only those books whose string appears in the list will be kept.

Return value description

MML text for the article, of the form "<Help>...</Help>". NULL if the article can't be found.

 

// get it

var vList = HelpArticle (Actor, ArticleName, Books);

if (!vList)

    return NULL;

 

// make up the string for the up-option

// potentially show "up" option

var vUp = vList[1];

if (vUp.StringLength())

    vUp = " \"" + ToStringMML(vUp) + "\"";

 

vUp = "<p/><p align=Center><a href=\"" +

    ToStringMML (ToString(sHelpContents) + vUp) +

    "\"><bold>" +

    ToStringMML (sHelpContentsUp) +

    "</bold></a></p>";

 

// find the ending MML in the list

var vRet = vList[0];

var vSearch = "</MML>";

var vFind = vRet.StringSearch (vSearch);

 

if (vFind >= 0)

    vRet.StringInsert (vUp, vFind);

 

// add in back

var vBack;

if (Actor && IsList(Actor.pHelpHistory) && (Actor.pHelpHistory.ListNumber() >= 2)) {

    vBack = "<p/><p align=Center><a href=\"" +

           ToStringMML (sHelpBackCommand) +

           "\"><bold>" +

           ToStringMML (sHelpBackShow) +

           "</bold></a></p>";

          

    vFind = vRet.StringSearch (vSearch);

    if (vFind >= 0)

           vRet.StringInsert (vBack, vFind);

}

 

 

return vRet;

HelpContents

Returns the directory of articles at the given level of contents.

 

Returns the directory of articles at the given level of contents.

 

Articles are organized into directories, just like the file system. Each article is placed somewhere in the directory structure, such as "Major category/minor category/sub category". (Note the FORWARD slash.)

 

Calling HelpContents() with "" will return all articles at the root of the directory structure, and a list of all sub-directories.

 

Calling with "Major Category" will return all articles and sub-directories under "Major Category"; this will include "Minor Category".

 

Parameter Name

Description

Actor

The actor looking in help. Some actors, either because of skills or access priviledges, won't be able to see some help topics.

Directory

Directory to enumerate. This can be "" to enumerate the root, or a series of names separated by a forward slash ('/').

Books

Controls what books of help will be searched.

 

If this is NULL, then only help articles in the default book, "", will be searched. (NULL will usually be passed in.)

 

If it's a string then only that book will be searched.

 

If it's a list of strings, then only those books whose string appears in the list will be kept.

Return value description

NULL if there's an error, or a list if it succedes.

 

The first element of the list is a list of all the articles in the directory. Each article is a list of its own, with the first element being the article name, and the second being a short description of the article.

 

The second element of the main list is a list of sub-directories, each sub-directory being a string.

 

HelpContentsMML

Returns the directory of articles at the given level of contents, as a resource string.

 

Returns the directory of articles at the given level of contents, as a resource string. Unlike HelpContents(), you can pass this string to the client to have it display the help.

 

Parameter Name

Description

Actor

The actor looking in help. Some actors, either because of skills or access priviledges, won't be able to see some help topics.

Directory

Directory to enumerate. This can be "" to enumerate the root, or a series of names separated by a forward slash ('/').

Books

Controls what books of help will be searched.

 

If this is NULL, then only help articles in the default book, "", will be searched. (NULL will usually be passed in.)

 

If it's a string then only that book will be searched.

 

If it's a list of strings, then only those books whose string appears in the list will be kept.

Return value description

NULL if couldnt find the contents, or a resource string of the form "<Help>...</Help>".

 

// get the list

var vList = HelpContents (Actor, Directory, Books);

if (!IsList(vList))

    return NULL;  // error

var vArticles = vList[0];

var vDir = vList[1];

   

// create a nice <Help>...</Help> resource string for this

var vRet = "<Help><LangID v=" + LanguageGet() + "/><MML>";

 

// loop over all the articles

var i;

var vItem;

if (vArticles.ListNumber()) {

    vRet += "<p><bold><big>" +

           ToStringMML (Directory.StringLength() ? sHelpContentArticlesWith.StringFormat(Directory) : sHelpContentArticles) +

           "</big></bold></p><ul>";

 

    // sort the articles alphabetically

    vArticles.ListSort   (SortSubListString);

   

    for (i = 0; i < vArticles.ListNumber(); i++) {

           vItem = vArticles[i];

          

           vRet += "<li><a href=\"" +

                  ToStringMML (ToString(sHelpArticle) + " \"" + ToStringMML(vItem[0]) + "\"") +

                  "\"><bold>" +

                  ToStringMML (vItem[0]) +

                  "</bold></a> - " +

                  ToStringMML (vItem[1]) +

                  "</li>";

    } // i

   

    vRet += "</ul><p/>";

} // if articles

 

// loop over all the directories

var vSuperDir;

if (vDir.ListNumber()) {

    vRet += "<p><bold><big>" +

           ToStringMML (Directory.StringLength() ? sHelpContentDirectoriesWith.StringFormat(Directory) : sHelpContentDirectories) +

           "</big></bold></p><ul>";

          

    vSuperDir = "" + Directory; // to make it into a new string

    if (vSuperDir.StringLength())

           vSuperDir += "/";

   

 

    // sort all the directories

    vDir.ListSort();

          

    for (i = 0; i < vDir.ListNumber(); i++) {

           vItem = vDir[i];

          

           vRet += "<li><a href=\"" +

                  ToStringMML (ToString(sHelpContents) + " \"" + ToStringMML(vSuperDir + vItem) + "\"") +

                  "\"><bold>" +

                  ToStringMML (vItem) +

                  "</bold></a></li>";

    } // i

   

    vRet += "</ul><p/>";

} // if vDir

 

// potentially show "up" option

var vFind;

if (Directory.StringLength()) {

    vFind = -1;

    while (TRUE) {

           i = Directory.StringSearch ("/", vFind+1);

           if (i >= 0)

                  vFind = i;

           else

                  break;

    }

   

    if (vFind >= 0)

           vFind = " \"" + ToStringMML(Directory.StringSubString (0, vFind)) + "\"";

    else

           vFind = "";   // if going up to top

 

    vRet += "<p align=center><a href=\"" +

           ToStringMML (ToString(sHelpContents) + vFind) +

           "\"><bold>" +

           ToStringMML (sHelpContentsUp) +

           "</bold></a></p>";

}

 

// add in a search

vRet +=

    "<p align=center><bold><button accel=enter href=\"" +

    ToStringMML(sHelpContentsMMLSearchCmd.StringFormat("<search>")) +

    "\">" +

    ToStringMML(sHelpContentsMMLSearchShow) +

    "</button><font color=#000000><edit defcontrol=true width=50% name=search/></font></bold></p>";

 

// add in back

var vBack;

if (Actor && IsList(Actor.pHelpHistory) && (Actor.pHelpHistory.ListNumber() >= 2)) {

    vBack = "<p/><p align=Center><a href=\"" +

           ToStringMML (sHelpBackCommand) +

           "\"><bold>" +

           ToStringMML (sHelpBackShow) +

           "</bold></a></p>";

          

    vRet += vBack;

}

 

vRet += "</MML></Help>";

 

return vRet;

HelpSearch

Searches through the help articles for a match against the keywords.

 

Searches through the help articles for a match against the keywords.

 

Parameter Name

Description

Actor

The actor looking in help. Some actors, either because of skills or access priviledges, won't be able to see some help topics.

Keywords

List of keywords to search for, separated by space.

Books

Controls what books of help will be searched.

 

If this is NULL, then only help articles in the default book, "", will be searched. (NULL will usually be passed in.)

 

If it's a string then only that book will be searched.

 

If it's a list of strings, then only those books whose string appears in the list will be kept.

Return value description

NULL if there's an error, or a list if it succedes.

 

The list is a list of articles that match. Each article is a sub-list with the article's name, a short description of the article, and an integer score. The list is sorted so that the best scores are first.

 

HelpSearchMML

Searches through the help articles for a match against the keywords, returning MML resource text.

 

Searches through the help articles for a match against the keywords, returning MML resource text. The resource text can then be sent to the client.

 

 

Parameter Name

Description

Actor

The actor looking in help. Some actors, either because of skills or access priviledges, won't be able to see some help topics.

Keywords

List of keywords to search for, separated by space.

Books

Controls what books of help will be searched.

 

If this is NULL, then only help articles in the default book, "", will be searched. (NULL will usually be passed in.)

 

If it's a string then only that book will be searched.

 

If it's a list of strings, then only those books whose string appears in the list will be kept.

Return value description

NULL if couldnt find anything to search for, or a resource string of the form "<Help>...</Help>".

 

// get the list

var vList = HelpSearch (Actor, Keywords, Books);

if (!IsList(vList))

    return NULL;  // error

if (!vList.ListNumber())

    return NULL; // nothing found

   

// create a nice <Help>...</Help> resource string for this

var vRet = "<Help><LangID v=" + LanguageGet() + "/><MML>";

 

// loop over all the found

var i;

var vItem;

vRet += "<p><bold><big>" +

    ToStringMML (sHelpSearch) +

    "</big></bold></p><ol>";

    // sort the articles alphabetically

 

for (i = 0; i < vList.ListNumber(); i++) {

    vItem = vList[i];

   

    vRet += "<li><a href=\"" +

           ToStringMML (ToString(sHelpArticle) + " \"" + ToStringMML(vItem[0]) + "\"") +

           "\"><bold>" +

           ToStringMML (vItem[0]) +

           "</bold></a> - <italic>(" +

           floor (vItem[2] / 1000) +

           "%)</italic> " +

           ToStringMML (vItem[1]) +

           "</li>";

} // i

 

vRet += "</ol>";

 

 

// add in back

var vBack;

if (Actor && IsList(Actor.pHelpHistory) && (Actor.pHelpHistory.ListNumber() >= 2)) {

    vBack = "<p/><p align=Center><a href=\"" +

           ToStringMML (sHelpBackCommand) +

           "\"><bold>" +

           ToStringMML (sHelpBackShow) +

           "</bold></a></p>";

          

    vRet += vBack;

}

 

vRet += "</MML></Help>";

 

return vRet;

NLPNounCase

Parses a noun-case string based upon the case settings.

 

This parses a noun-case string based upon the case settings...

 

A noun-case string is a string containing the name of an object. The string also has extra tags to identify how the name changes depending upon the "case" of the noun. For example: A noun case string for a "person" object would be "(sing?person:people)"; the "sing" followed by a '?' means that if the requested case is singular then return the first option, "person". If the requested case is not singular (plural) then return "people". This is a simple example using English. Many languges are highly inflected, and can get more complicated.

 

In the noun-case string, a test follows the form, "(X?Y:Z)", where X is the noun-case tested (see below), Y is the string that's used if the test passes, and Z is used if the test fails. The Z string is not necessary; the noun-case string could just be "(X?Y)", in which case if the X case does not pass, then no string will used.

 

Tests can be placed inside one another. Therefore, "(X?(A?B:C):Z)" is possible. Tests can also be placed in sequece and include sub-strings that are always used: "M(X?Y:Z)N(A?B:C)O".

 

The noun case passed in is a number that is an or-ed combination of the gNC_XXX_YYY global variables. (Do not change thse variables, since they're supposed to be constant values.) To completely describe the noun form, use one of each XXX type, except for the _Mask ending. This would mean: gNC_Art_YYY, gNC_Count_YYY, gNC_Anim_YYY, gNC_Fam_YYY, gNC_Verbose_YYY, gNC_Gender_YYY, gNC_Caps_YYY, gNC_Person_YYY, gNC_Quantity_YYY, and gNC_Case_YYY. If you don't specify a case from the given XXX category then a default will be used.

 

gNC_Art_YYY lets you control if there's an article in front of the noun. gNC_Art_None is for no article, gNC_Art_Definite uses "the" (in English). gNC_Art_Indefinite uses "a" or "an" (in English).

 

gNC_Count_YYY controls the count aspect of the noun. gNC_Count_Single creates a singular form, gNC_Count_Few is for a plural form with two to three objects, and gNC_Count_Many is for plural objects with four or more objects. (Few and Many are necessary since some languages have two forms of plural.)

 

gNC_Anim_YYY specifies if the noun is animate or inanimate: gNC_Anim_Animate or gNC_Anim_Inanimate. English does not use these.

 

gNC_Fam_YYY controls the familiar and formal forms: gNC_Fam_Familiar and gNC_Fam_Formal. Many languages have familiar and formal pronouns. English does not.

 

gNC_Verbose_YYY specifies if the name is the long or short form: gNC_Verbose_Short or gNC_Verbose_Long. Some objects will have a long form "John Alva Edison" or a short form "John", depending upon how they're used in speech.

 

gNC_Gender_YYY can be gNC_Gender_Male, gNC_Gender_Female, or gNC_Gender_Neuter.

 

gNC_Caps_YYY causes the first letter of the noun to be capitalized. It can be gNC_Caps_Upper or gNC_Caps_Lower.

 

gNC_Person_YYY defines 1st (I, we), 2nd (you, you-plural), or 3rd person (he, they). It can be gNC_Person_First, gNC_Person_Second, or gNC_Person_Third.

 

gNC_Quantity_YYY indicates whether a quantity number should be shown in front of the noun. gNC_Quantity_NoQuantity (default) doesn't show a quantity, while gNC_Quantity_Quantity will show a quantity. (Note: NLPNounCase() does NOT handle the display of quantities.)

 

gNC_Case_YYY controls the linguistic "noun case". English has three cases: gNC_Case_Objective (such as "me", "him", or "her"), gNC_Case_Subjective (such as "I", "he", or "she"), and gNC_Case_Possessive (such as "My", "his", or "her"). Other languages have more cases. A full list can be seen by looking for global variables beginning with "gNC_Case_".

 

When a noun-case string includes the name of the case to check, it can be reduced to the first 2-4 characters of the case. There's no need to type the whole word. Example: "(Obj?me:I)" will test to see if the noun is gNC_Case_Objective. If it is then "me" will be othed, otherwise "I". Enough letters need to be used from the start of the name so that it can be identified from another case. If you're not sure, use the first 4 characters.

 

Parameter Name

Description

NounCaseString

String that includes tests based upon the case.

NounCase

Case of the noun. One or more gNC_XXX_YYY or-ed together. For example: (gNC_Gender_Male | gNC_Count_Singular | gNC_Case_Objective)

AppendNounCase

(Optional) If TRUE (or not specified), this appends the noun-case information to the string in the form of "{X}", where X is the NounCase value that is used for noun-verb agreement. Appending this information now makes the call into NLPVerbForm() or NLPStringFormat() easier.

Return value description

The string that results from the tests in the NounCaseString and the NounCase settings.

 

NLPParse

This parses a NLP sentence and produces hypothesis.

 

NLPParse() accepts a natural language sentence, such as a command or something the user says to a NPC.

 

It then uses a series of NLPRuleSet rules (see the documentation) that were added through NLPRuleSetAdd() calls. Rules disabled with NLPRuleSetEnableSet() are ignored. The language used is determined by the current language. (See LanguageSet()).

 

The sentence is analyzed using the NLP rules, producing a list of hypothesis for what was said (potentially 100's - 1000's). The hypothesis are simplified versions of original sentence. For example: "Could you tell me who Mike is?" and "Who is Michael?" could both be simplified down to "`WHOIS Mike". (Other hypothesis will also be generated.)

 

The returned hypothesis can be passed to a more specific parser, such as one for interpreting commands or interpreting speech to a NPC.

 

 

Parameter Name

Description

Parser

The name of the parser to use. Use NLPParserEnum() to obtain a list of parsers.

TemporaryRules

These are temporary rules that will be used along with the rules in the parser's rule sets. The format is a MML string with "<NLPRuleSet>...</NLPRuleSet>", defining the meaning of the rules. For more information  onthe format see NLPRuleSet.

 

You can use the temporary rules for word/phrase meanings that change every time the parser is called, such as pronouncs. After all, the meaning of "it" changes depending upon the last few sentences.

 

If you don't wish to use any temporary rules then pass in NULL.

Sentence

This is the sentence that's to be parsed.

Return value description

This returns a list of hypothesis.

 

Each hypothesis is a list. The first element in the list is the probability (or score) of the hypothesis, from 0..1. The next elements are a series of hypothesized sentences. Most hypothesis will only contain one sentence, but some may be more than one.

 

Each sentence is a list of words, such as "who", "is", "mike". If any word began with a '|' character and was followed by an object ID, the original string will automatically be converted to an object ID. If any strings begins with a '`' then it is automatically lower cased.

 

NLPParserClone

Duplicates a NLP parser.

 

Use this to clone a NLP parser.

 

You may wish to clone a NLP parser when a new NPC appears on the scene. A parser common to all NPCs could be set up, and then if a NPC uses a slightly different set of rules, clone the common parser and modify it to fit the NPC.

 

Parameter Name

Description

Orig

The name of the parser to clone. Use NLPParserEnum() to obtain a list of parsers.

CloneTo

Clone the parser to this.

Return value description

TRUE if the parser was found and cloned. FALSE if the parser could not be found.

 

NLPParserEnum

Enumerates the NLP parsers.

 

This enumerates what NLP parsers are running in the VM. You will probably have one parser for dealing with commands from the user, and one or more for parsing NLP conversations with NPCs.

 

Parameter Name

Description

Return value description

Returns a list containing all the parsers' names.

 

NLPParserRemove

Removes a NLP parser.

 

This deletes a NLP parser when it is no longer being used.

 

Parameter Name

Description

Parser

The name of the parser to remove. Use NLPParserEnum() to obtain a list of parsers.

Return value description

TRUE if the parser was found and removed. FALSE if the parser could not be bound.

 

NLPPronoun

Returns a pronoun string.

 

Uses the NounCase parameter to determine what pronoun string to return, such as "I" vs. "me".

 

Parameter Name

Description

NounCase

One or more flags or-ed together. The flags are from the gNC_XXX_YYY constants. For more information see NLPNounCase().

AppendNounCase

(Optional) If TRUE (or not specified), this appends the noun-case information to the string in the form of "{X}", where X is the NounCase value that is used for noun-verb agreement. Appending this information now makes the call into NLPVerbForm() or NLPStringFormat() easier.

Return value description

Pronoun string.

 

NLPRuleSetAdd

Adds a new rule set to a given parser.

 

Call this to add a rule set to a given parser. The rule set starts out enabled.

 

NOTE: Added rule sets are NOT saved when the VM is saved to disk. Therefore, any objects or code that rely on a rule set MUST reload it when the VM is reloaded from disk.

 

Parameter Name

Description

Parser

The name of the parser to look in. Use NLPParserEnum() to obtain a list of parsers.

 

If the parser does not already exist then it will be added to the list.

RuleSet

The name of the rule set to add. If the rule set already exists then the existing one is overwritten.

Rules

                                     These are the rules to add. If they are a resource they must be of the type, "NLPRuleSet". If they are a string, they must be MML with "<NLPRuleSet>...</NLPRuleSet>". (Search in the documentation for more information on NLPRuleSet.)

 

If the Rules parameter is a resource or it's a string token, NLPRuleSetAdd() will see if the resource or string token supports multiple languages. If it does then all of the supported languages will be added at once. The proper language will be selected when NLPParse() is called.

 

Otherwise, the rule set will only work for one language, as specified by the current language. (See LanguageSet())

Return value description

TRUE if the rule set is added, FALSE if there's an error.

 

NLPRuleSetEnableAll

Enables or disables all rule sets in a parser.

 

Enables or disables all rule sets in a parser.

 

Parameter Name

Description

Parser

The name of the parser to look in. Use NLPParserEnum() to obtain a list of parsers.

Enable

If TRUE then enable all rule sets in the parser. If FALSE then disable all rule sets.

Return value description

TRUE if the rule sets are enabled, FALSE if it's disabled. If the parser cannot be found it returns NULL.

 

NLPRuleSetEnableGet

Returns TRUE if the rule set is enabled.

 

This tests to see if a rule set is enabled, returning TRUE or FALSE.

 

 

Parameter Name

Description

Parser

The name of the parser to look in. Use NLPParserEnum() to obtain a list of parsers.

RuleSet

The name of the rule set to test. Use NLPRuleSetEnum() to obtain a list of rule sets in a parser.

Return value description

TRUE if the rule set is enabled, FALSE if it's disabled. If the rule set cannot be found it returns NULL.

 

NLPRuleSetEnableSet

Enables or disables a rule set in a parser.

 

This enables or disables a rule set in a parser. Disabling a rule set causes it not to be used when NLPParse() is called.

 

Parameter Name

Description

Parser

The name of the parser to look in. Use NLPParserEnum() to obtain a list of parsers.

RuleSet

The name of the rule set to set. Use NLPRuleSetEnum() to obtain a list of rule sets in a parser.

Enable

Pass in TRUE to enable, FALSE to disable.

Return value description

TRUE if success, FALSE if the rule set could not be found.

 

NLPRuleSetEnum

Enumerates the rule sets in a given parser.

 

This returns a list of all the rule sets in a given parser.

 

Parameter Name

Description

Parser

The name of the parser to look in. Use NLPParserEnum() to obtain a list of parsers.

Enabled

If TRUE then enumerate only enabled rule sets. If FALSE then enumerate only disabled rule sets. If NULL then enumerate all rule sets, enabled or disabled.

Return value description

A list containing string-based names of all the rule sets in the parser. If the parser could not be found the function will return FALSE.

 

NLPRuleSetExists

Returns TRUE if the rule set exists.

 

Returns TRUE if the rule set exists.

 

Parameter Name

Description

Parser

The name of the parser to look in. Use NLPParserEnum() to obtain a list of parsers.

RuleSet

The name of the rule set to test. Use NLPRuleSetEnum() to obtain a list of rule sets in a parser.

Language

Langauge ID (from  LanguageGet()) to look for. If this is NULL then any language is acceptable.

Return value description

TRUE if the rule set exists. FALSE if it does not.

 

NLPRuleSetRemove

Removes a rule set from a given parser.

 

This removes a rule set from a given parser.

 

Parameter Name

Description

Parser

The name of the parser to look in. Use NLPParserEnum() to obtain a list of parsers.

RuleSet

The name of the rule set to remove. Use NLPRuleSetEnum() to obtain a list of rule sets in a parser.

Return value description

TRUE if the rule set was removed. FALSE if it could not be found.

 

NLPStringFormat

Replaces %1, %2 in a string, and calls NLPVerbForm().

 

This useful function replaces occurances of "%1", "%2", etc. in a string with new substrings. (See the StringFormat() method), and then calls NLPVerbForm() to convert noun-case tags (enclosed in left and right braces) and verb-form tags (enclosed in parenthesis).

 

The NLPStringFormat() function allows for easy insertion of object names into a string. Example: NLPStringFormat ("%1 (am/are/is/are) in good shape.", object.NLPName (actor, gNC_Case_Subjective)) will result in %1 being replaced with the object name, and the correct verb form being chosen.

 

You might wish to use NLPStringFormat2(), which has the same functionality but allows objects to reword the String to meet specific conditions.

 

Parameter Name

Description

String

String with %1, %2, etc. in it.

 

If this is a list, a random element will be selected from the list for speaking.

 

If this is an object, the object is returned. The passthrough happens to enable SpeakScript() to take cConvStory (potentially associated with cKnowledge) and cConvTree objects.

Param1

If %1 is found, it is replaced by Param1.

Param2

If %2 is found it is replaced by Param2.

Return value description

New string that includes the replacements.

 

// select from list

while (IsList(String))

    String = Random(String);

 

if (IsObject(String))

    return String;

 

var sReplace = String.StringFormat (arguments[1], arguments[2],

    arguments[3], arguments[4], arguments[5], arguments[6], arguments[7],

    arguments[8], arguments[9], arguments[10]);

   

return NLPVerbForm (sReplace);

NLPStringFormat2

Replaces %1, %2 in a string, and calls NLPVerbForm().

 

This function is like NLPStringFormat(), except that:

 

- It takes slightly different parameters, allowing you to pass in sub-lists with objects instead of using NLPName() all the time.

 

- For each object mentioned, as well as the room that the Actor is in, StringReword() is called to see if the string should be customized for a specific case. For example: You might want to change the response of, "You pick up the jewel." to "With greedy eyes, you carefully pick up the jewel."

 

This useful function replaces occurances of "%1", "%2", etc. in a string with new substrings or names of objects.

 

Parameter Name

Description

Actor

Player character that this string will be displayed to.

String

String with %1, %2, etc. in it.

 

If this is a list, a random element will be selected from the list for speaking.

Replace1

This replaces %1 in the string. If it's a string, the replacement is direct. If it's a list, the first parameter is the object name to display, the second is the gNC_XXX_YYY parameters. The list normally has only two items, but if there's a 3rd and 4th item, the 3rd is the possessor of the object (1st item), and the 4th is the gNC_XXX_YYY parameters for the possessor.

Replace2

Like Replace1.

Replace3

Like Replace1.

Replace4

Like Replace1.

Replace5

Like Replace1.

Return value description

The new string.

 

var vReplace = [Replace1, Replace2, Replace3, Replace4, Replace5];

var vNum = vReplace.ListNumber();

var i, vList, vRet;

 

// actor

if (vRet = Actor.StringReword (Actor, String, Replace1, Replace2, Replace3, Replace4, Replace5))

    String = vRet;

 

// room

var vRoom = ActorToRoom (Actor, TRUE);

if (vRoom && (vRet = vRoom.StringReword (Actor, String, Replace1, Replace2, Replace3, Replace4, Replace5)))

    String = vRet;

   

// call the objects

for (i = 0; i < vNum; i++) {

    // only care if its a list

    if (!IsList(vList = vReplace[i]))

           continue;

          

    // get the string

    if (IsObject(vList[2])) {

           if (vRet = vList[0].StringReword (Actor, String, Replace1, Replace2, Replace3, Replace4, Replace5))

                  String = vRet;

                 

           if (vRet = vList[2].StringReword (Actor, String, Replace1, Replace2, Replace3, Replace4, Replace5))

                  String = vRet;

    }

    else

           if (vRet = vList[0].StringReword (Actor, String, Replace1, Replace2, Replace3, Replace4, Replace5))

                  String = vRet;

} // i

 

// change the sub-lists to strings

for (i = 0; i < vNum; i++) {

    // only care if its a list

    if (!IsList(vList = vReplace[i]))

           continue;

   

    // get the string

    if (IsObject(vList[2]))

           vReplace[i] = vList[0].NLPNamePossessed (Actor, 0, vList[2], vList[3]);

    else

           vReplace[i] = vList[0].NLPName (Actor, vList[1]);

   

} // i

 

return NLPStringFormat (String, vReplace[0], vReplace[1], vReplace[2], vReplace[3], vReplace[4]);

NLPVerbForm

Conjugates verbs in a sentence.

 

This conjugates verbs in a sentence. NLPVerbForm() is useful for dealing with verb forms when "filling in the blank" for a strink "%1 (am/are/is) in good health.", since the verb could be conjugated to "I am in good health.", "You are in good health.", "He is in good health.", or "They are in good health."

 

The function accepts a string containing a sentence with special tags indicating what class each noun is, along with the verbs of each form. It uses to noun tags to determine what forms the verb should take.

 

Each noun (that could affect a to-be-conjugated verb) must have a tag next to it. The tag is a number surrounded by curly braces, { and }. The number is the values of gNC_XXX_YYY or-ed together. See NLPNounCase() for a description of the flags. Example: "{345321}"

 

Each verb (that can be conjugated) must be in parenthesis with the alternative forms of the verb separated by slashes. The forms are language dependent. In English, the forms are 1st-person singular, 2nd-person singular and all plurals, and 3rd-person singular. Example: "(am/are/is)" or "(walk/walk/walks)").

 

The function returns a string with the noun case numbers removed, and the verbs conjugated.

 

In order to do the processing, this function calls into NLPVerbFormCallback(). NLPVerbFormCallback() already supports Enlgish. If you wish to use Circumreality for another language you many need to modify this function.

 

Parameter Name

Description

String

The string with noun and verb tags.

Return value description

A string the verbs conjugated, and noun and verb tags removed.

 

NLPVerbFromCallback

Callback from NLPVerbForm()

 

NLPVerbFormQuery() is called by NLPVerbForm() to determine how to conjugate a verb. NLPVerbFormCallback() already supports English. If you are porting Circumreality to another language (such as Swedish) then you may need to modify this function.

 

NLPVerbFormCallback() is passed a list of all the nouns and verbs in the sentece. The nouns are represented by their noun-case value (See NLPNounCase() for a description). The verbs are initially filled in with 0.

 

Example: If "He{34532} (am/are/is/are) in good shape." where passed into NLPVerbForm(), NLPVerbFormQuery() would be passed the following list: [34532, 0].

 

NLPVerbFormCallback() needs to find all the 0-values, indicating verbs, and look at the neighboring noun cases to determine what form should be used. It then fills in a NEGATIVE number for the verb form.

 

Verb forms are language independing. In English, however, the following forms are used:

 

-1 : 1st person singular

-2 : 2nd person singular, and plural

-3 : 3rd person singular

 

Parameter Name

Description

FormList

This originally contains a list of noun-case values and 0's where the verb form must be filled in. NLPVerbFormCallback() should change all the 0's to negative values indicating their verb form.

Return value description

None

 

PerformanceCPU

Returns the amount of CPU used by the process.

 

Returns the amount of CPU used by the process.

 

To use this, you'll need to occasionally poll the CPU and subtract values over a given time segment.

 

Parameter Name

Description

Return value description

List with [Time since creation, Time in process, Main thread's time since creation, Main thread's time in process].

 

Time since creation - The number of seconds since the creation of the process.

 

Time in process - The total time spend in the process since the process was created, divided by the number of processors in the system. In seconds.

 

Main thread's time since creation - Number of seconds the main thread has existed.

 

Main therad's time in process - Number of seconds the main thread has run, NOT divided by the number of processors.

 

Note: The main thread's time information is returned since it's the thread that uses the most CPU. If you are using a multicore or SMP machine, the main thread will be the gating factor for the maximum number of users on the system. Once this reaches around 100% utilization you won't be able to get any more users on, even if you have spare capacity in the other processors.

 

PerformanceCPUObject

Keeps track of how much CPU an object uses.

 

This keeps track of how much CPU an object uses. More specifically, it monitors CPU usage on the main thread, assuming that the main thread is the bottleneck for perfomance.

 

You can use this to keep track of how much CPU (approximately) an individual player is using by passing the player's character in as the object.

 

This starts monitoring the current object. It assumes that the CPU from here on out is being used by the current object, UNTIL PerformanceCPUObject() is called again, or the callback (or timer) finishes.

 

Parameter Name

Description

Object

Object to be monitored, usually the player's character. If this is NULL, the monitoring of the current object stops.

Return value description

None

 

PerformanceCPUObjectQuery

Returns the percentage of CPU used by the object.

 

Used in conjuction with PerformanceCPUObject(), this returns the percentage of CPU being used by the given object. The timerframe is over the last 60 seconds (approximately).

 

Parameter Name

Description

Object

Object to query, usually the player's character.

Return value description

Percentage (0.0 .. 1.0) of CPU (for the main thread only) that has been used by this object. Of course, the number will only be accurate if PerformanceCPUObject() was called with the object.

 

PerformanceDisk

Returns the amount of free space on the database disk.

 

Returns the amount of free space on the database disk.

 

Parameter Name

Description

Return value description

Returns the free space in gigabytes on the database disk.

 

PerfomanceGUI

Returns the number of GUI objects allocated to the process.

 

Returns the number of GUI objects allocated to the process. This can be used to detect slow memory leaks.

 

Parameter Name

Description

Return value description

Returns the number of GUI objects.

 

PerformanceHandles

Returns the number of handles used on the server.

 

Returns the number of handles used on the server. This can be used to detect slow memory leaks.

 

Parameter Name

Description

Return value description

Returns the number of handles.

 

PerformanceMemory

Returns the amount of memory allocated by the server.

 

Returns the amount of memory allocated by the server.

 

Parameter Name

Description

MemoryType

Type of memory information that want...

 

0 - The amount of memory specifically allocated by the server code.

1 - The amount of memory allocated by the memory manager (which is more than the amount allocated by the server code.)

2 - Current working set.

3 - Amount of memory used by the process, in total. This includes pooled and non-pooled.

Return value description

See MemoryType. Values are in megabytes.

 

PerformanceNetwork

Returns the amount of data sent/received from the server over the Internet.

 

Returns the amount of data sent/received from the server over the Internet.

 

Parameter Name

Description

Return value description

List with [Megabytes sent, Megabytes received].

 

Note: Megabyte = 1,000,000 bytes exactly.

 

PerformanceThreads

Returns the number of threads used on the server.

 

Returns the number of threads used on the server. This can be used to detect slow memory leaks.

 

Parameter Name

Description

Return value description

Returns the number of threads.

 

RenderCacheLimits

Limits how much memory and disk space is allocated to the render cache.

 

The server accepts rendered images and text-to-speech recordings from the players' computers and caches them away. These are then sent out to other players, saving them processing.

 

Use this function to set the limits of how large the cache will be allowed to get.

 

Parameter Name

Description

DataMaximum

Maximum amount of data that will be cached, in megabytes.

DataMinimumHardDrive

If there isn't much hard drive left, the cache will shrink itself. This is the minimum amount of hard drive (in megabytes) will be kept available. If the free hard drive is less than this, the cache will be deleted until that much is free.

DataGreedy

When the server first starts up with a new MIF file, it will erase the existing cache.

 

In order to fill the cache quickly, if the cache size is less than DataGreedy (in megabytes) then all rendered images and text-to-speech are accepted from players. Normally, data is only occasionally accepted.

DataMaxEntriesOn32

Maximum number of entries that will be allowed on a 32-bit server (around 10000). This number has to be somewhat small so that the memory for the index doesn't use up all the limited 2 gigabyte limit.

DataMaxEntriesOn64

Maximum number of entries that will be allowed on a 64-bit server (around 1000000).

Return value description

None

 

SavedGameEnum

Enumerates all the saved games.

 

Enumerates all the saved games.

 

Parameter Name

Description

FileName

"File name" for the saved game. This must be short (less than 64 characters) and not contain any illegal windows file-name characters, such as \.

Return value description

Returns a list of saved games. Each saved game is a list with the first element being the game name, followed by the creation time, modification time, and last access time.

 

SavedGameFilesDelete

Deletes a save-game file, and all of its sub-files.

 

Deletes a save-game file, and all of its sub-files.

 

Parameter Name

Description

FileName

Saved-game file to delete.

Return value description

TRUE if the file was deleted. FALSE if it doesn't exist.

 

SavedGameFilesEnum

Enumerates all the saved-game files.

 

Enumerates all the saved-game files.

 

Parameter Name

Description

Return value description

Returns a list of strings, one for each saved-game filename.

 

SavedGameFilesName

Returns the name of a saved-game file given its index.

 

Returns the name of a saved-game file given its index.

 

Parameter Name

Description

Index

Index, from 0 .. SavedGamesFilesNum()-1.

Return value description

Returns a string with the name, or NULL if it can't be found.

 

SavedGameFilesNum

Returns the number of save-game files.

 

Returns the number of save-game files.

 

Parameter Name

Description

Return value description

Returns the number of saved-game files.

 

SavedGameInfo

Returns date/time stamp information about a sub-file.

 

Returns date/time stamp information about a sub-file.

 

Parameter Name

Description

FileName

"File name" for the saved game. This must be short (less than 64 characters) and not contain any illegal windows file-name characters, such as \.

SubFileName

Name of the sub-file.

Return value description

Returns a list with [creation time, modification time, last access time].

 

If the sub-file doens't exist then this returns FALSE.

 

SavedGameLoad

Loads in a saved game.

 

Loads in a saved "game" that was saved by SaveGameSave().

 

Parameter Name

Description

FileName

"File name" for the saved game. This must be short (less than 64 characters) and not contain any illegal windows file-name characters, such as \.

SubFileName

Name of the game to load.

RemapObjects

If an object is loaded and it doesn't currently exist then it keeps its old ID.

 

However, if an object already exists then: If RemapObjects is TRUE then a new ID will be given to the object (and returned; see below). If FALSE then the current object will be replaced with the saved object.

 

Furthermore, if RemapObjects == FALSE and the game was saved with SavelAll == TRUE then any deleted objects will also be deleted when the game is loaded.

Return value description

If RemapObjects == TRUE then this will return a list of all the objects remapped when they were loaded. Each remapping is a list with the first element being the original object (when it was saved), and the second element being the new object (now that it's loaded).

 

Otherwise, this returns TRUE on success, or FALSE on failure.

 

SavedgameName

Given an index into a saved-game file, this returns the sub-file name.

 

Given an index into a saved-game file, this returns the sub-file name.

 

Parameter Name

Description

FileName

"File name" for the saved game. This must be short (less than 64 characters) and not contain any illegal windows file-name characters, such as \.

Index

Index, from 0 .. SavedGameNum()-1.

Return value description

Returns the sub-file's name, or NULL if the index exceeds the number of sub-files.

 

SavedGameNum

Returns the number of saved game sub-files in a saved-game file.

 

Returns the number of saved game sub-files in a saved-game file.

 

Parameter Name

Description

FileName

"File name" for the saved game. This must be short (less than 64 characters) and not contain any illegal windows file-name characters, such as \.

Return value description

Returns the number of sub-files.

 

SavedGameRemove

Deletes a saved game.

 

Deletes a saved game.

 

Parameter Name

Description

FileName

"File name" for the saved game. This must be short (less than 64 characters) and not contain any illegal windows file-name characters, such as \.

SubFileName

Name of the game to delete.

Return value description

TRUE if the game was deleted. FALSE if the game name wasn't found.

 

SavedGameSave

Saves a game.

 

This saves a "game". It does so by saving a set of objects. NOTE: It does NOT save any global variables, so no game information that changes should be placed in a global variable at save time.

 

You can also use it to save a collection of objects, used for instancing.

 

Parameter Name

Description

FileName

"File name" for the saved game. This must be short (less than 64 characters) and not contain any illegal windows file-name characters, such as \.

SubFileName

Name of the game to save. If a game already exists it will be overwritten.

SaveAll

If TRUE, save all objects except those in the list. It will also note which objects have been deleted and save that information so when the data is reloaded it will delete objects that had been deleted.

 

If FALSE then save only those objects in the list.

 

NOTE: You shouldn't save any of the connection objects because when you reload the game the current connection information will be overwritten. The same goes for other objects that depend on run-time.

SaveChildren

If TRUE, automatically expand the list to include children (recursively) of the objects in the list.

ObjectList

List of objects to exclude or include. This can be an empty list if SaveAll == TRUE.

Return value description

TRUE if the game was deleted. FALSE if the game name wasn't found.

 

ShardParam

Returns a parameter indicating what shard this is.

 

This returns a parameter indicating what shard this is.  It returns the string specified in the TitleInfo resource for the running shard. If the server is running on the hose (no Internet support) then it returns "offline".

 

Parameter Name

Description

Return value description

String with the shard's parameter.

 

ShardParamIsOffline

Returns TRUE if this world is runing offline.

 

Returns TRUE if this world is runing offline.

 

It calls ShardPram() and returns TRUE if the value is "offline".

 

NOTE: After this is called once, you can use gShardPramIsOffline for speed. Given all the objects that call this during initialization, you can be absolutely sure that AFTER initialization, gShardParamIsOffline is vlaid.

 

Parameter Name

Description

Return value description

TRUE if running offline. FALSE if online (and multiple users)

 

ShutDownImmediately

Causes the (near) immediate shutdown of the server.

 

This causes the (near) immediate shutdown of the server. This DOES NOT check in any objects that need checking in.

 

You may wish to call another ShutDownXXX() function that will give players some warning about how long they have before the shutdown occurs, and which will check in necessary objects.

 

Parameter Name

Description

Restart

If TRUE then the server will restart as soon as it has shut down. Use the automatic restart as a way to clean up memory.

Return value description

None

 

SortString

Internal sort callback that sorts by a string, case insensative.

 

Internal sort callback that sorts by a string, case insensative.

 

Parameter Name

Description

ItemA

First item to compare.

ItemB

Second item to compare.

Return value description

0 if the items are the same, positive if ItemA appears after itemB, and negative if vice versa.

 

// since the contents are strings, just compare the strings

return ItemA.StringCompare (ItemB, FALSE);

SortSubListString

Internal sort callback that sorts by first element (a string) in the sublist.

 

Internal sort callback that sorts by first element (a string) in the sublist.

 

Parameter Name

Description

ItemA

First item to compare.

ItemB

Second item to compare.

Return value description

0 if the items are the same, positive if ItemA appears after itemB, and negative if vice versa.

 

// since the contents are lists, just compare the first element of the

// list using a string compare.

return ItemA[0].StringCompare (ItemB[0], FALSE);

SortSubListValue

Internal sort callback that sorts by first element (a value) in the sublist.

 

Internal sort callback that sorts by first element (a value) in the sublist.

 

Parameter Name

Description

ItemA

First item to compare.

ItemB

Second item to compare.

Return value description

0 if the items are the same, positive if ItemA appears after itemB, and negative if vice versa.

 

// since the contents are lists, just compare the first element of the

// list using a string compare.

var v1 = ItemA[0];

var v2 = ItemB[0];

 

if (v1 < v2)

    return -1;

else if (v1 > v2)

    return 1;

else

    return 0;     // same

SortSubListValue2

Internal sort callback that sorts by second element (a value) in the sublist.

 

Internal sort callback that sorts by second element (a value) in the sublist.

 

Parameter Name

Description

ItemA

First item to compare.

ItemB

Second item to compare.

Return value description

0 if the items are the same, positive if ItemA appears after itemB, and negative if vice versa.

 

// since the contents are lists, just compare the first element of the

// list using a string compare.

var v1 = ItemA[1];

var v2 = ItemB[1];

 

if (v1 < v2)

    return -1;

else if (v1 > v2)

    return 1;

else

    return 0;     // same

TextLog

Logs a line of text.

 

This writes a line of text to the log. (You can also write text using TextLogNoAuto() and Trace()).

 

This uses the actor, room, and user set in TextLogAutoSet().

 

If the text log is disabled (TextLogEnableSet()), then this won't do anything.

 

Parameter Name

Description

Text

Text string to log.

Object

(Optional) If the log involves an object, such as oLantern, pass the object in here.

Return value description

TRUE if success. FALSE if an error occurs.

 

TextLogAutoGet

Returns a value set by TextLogAutoSet().

 

Returns a value set by TextLogAutoSet().

 

Parameter Name

Description

Parameter

This is either "actor" (for the player's character), "room" (for the current room), or "user" (for the user object).

Return value description

TRUE if success. FALSE if an error occurs.

 

TextLogAutoSet

Sets one of the automatic objects for TextLog().

 

In order to save work for the author, whenever the user sends a command, TextLogAutoSet() is called to remember the current PC (actor), user, and room the character is in. Then, any calls to TextLog() will automatically log the actor, user, and room.

 

Parameter Name

Description

Parameter

This is either "actor" (for the player's character), "room" (for the current room), or "user" (for the user object).

Object

Pass in an object, or NULL to clear the setting.

Return value description

TRUE if success.

 

TextLogDelete

Deletes a log file.

 

Deletes a log file.

 

Parameter Name

Description

LogID

This is an identifier string for the log, from TextLogEnum().

Return value description

TRUE if the file was deleted. FALSE if it wasn't. A log file won't be deleted if it's read-only, or if it's still in use.

 

TextLogEnableGet

Returns TRUE if text logging is enabled.

 

Returns TRUE if text logging is enabled.

 

Parameter Name

Description

Return value description

Returns TRUE if text logging is enabled.

 

TextLogEnableSet

Enables or disables text logging.

 

If you wish to disable text logging completely, such as for an offline game, then call TextLogEnableSet(FALSE).

 

Parameter Name

Description

Enable

TRUE to enable, FALSE to disable.

Return value description

None

 

TextLogEnum

Enumerates all the log files that still exist.

 

Enumerates all the log files that still exist.

 

The server's MIFL code will probably automatically delete old log files.

 

Parameter Name

Description

Return value description

Returns a list of log files. Each log file is a sub-list with [LogID, Date]. LogID is a string that identifies the log file, and which can be passed into a few LogTextXXX() functions. Date is the time/date (as per TimeGet()) that the log file covers.

 

TextLogNoAuto

Logs a line of text.

 

 

This writes a line of text to the log. (You can also write text using TextLog() and Trace()).

 

If the text log is disabled (TextLogEnableSet()), then this won't do anything.

 

Parameter Name

Description

Text

Text string to log.

Actor

Character object performing the action.

Object

(Optional) If the log involves an object, such as oLantern, pass the object in here.

Room

Room object the action takes place in.

User

User object (if specific to a user) where the object takes place.

Return value description

TRUE if success. FALSE if an error occurs.

 

TextLogNumLines

Returns the number of lines in a text log.

 

Returns the number of lines in a text log.

 

NOTE: This function is somewhat slow so don't call it too often. Try to cache the number.

 

Parameter Name

Description

LogID

This is an identifier string for the log, from TextLogEnum().

Return value description

The number of lines in the log, or FALSE if there's an error.

 

TextLogPriorityAdjust

Adjusts the gTextLogSystem and gTextLogUser globals.

 

Adjusts the gTextLogSystem and gTextLogUser globals. Both of these globals are used to determine which priority events get logged.

 

TextLogPriorityAdjust() should be called whenever a message is processed from a user, as well as at the end of the processing. It modifies gTextLogSystem and gTextLogUser globals based on gTextLogSystemAlert, as well as the connection's pUserLogAlert.

 

This also calls TextLogAutoSet() for the "user", "actor", and "room".

 

Parameter Name

Description

Connection

Connection object, with pUserLogAlert set based on the connection's user.

Return value description

None

 

TextLogRead

Reads a line from a log file.

 

Reads a line from a log file. You can find out the number of lines by calling TextLogNumLines().

 

 

Parameter Name

Description

LogID

This is an identifier string for the log, from TextLogEnum().

Line

Line number, from 0 to TextLogNumLines().

Return value description

If successful, this returns a list with [Time, String, Actor, Object, Room, User]. Time is the time (as per TimeGet()) when the event occurred. String, Actor, Object, Room, and User are the parameters passed into TextLog(), TextLogNoAuto(), or Trace().

 

TextLogSearch

Searches through the logs for specific events.

 

This searches through all the logs (from TextLogEnum()), for events involving a specific search string, actor, object, room, or user.

 

The search is asyncrhonous, so the results are passed into a callback.

 

The callback will be passed a list of search results. Each result is a sub-list with [LogID, LineNumber]. LogID is the log file identifier, like from TextLogEnum(). LineNumber is the line number that matches the criteria for the search (such as the string, actor, etc.)

 

Parameter Name

Description

StartTime

Starting time to search (from TimeGet()). Log entries before this will be ignored.

StopTime

Finishing time (from TimeGet()). Log entries after this will be ignored.

String

Sub-string to search for, case INsensative. This can be NULL, in which case all strings will be accepted.

Actor

Actor (character) that must be mentieond in the log. If this is NULL then all actors are allowed.

Object

Object that must be mentioned in the log line. This can be NULL.

Room

Room that must be mentioned in the log line. This can be NULL.

User

User that must be mentioned in the log line. This can be NULL.

Callback

Either a function or method (with object) that's called. This should accept two parameters. The first is the search results (see main description). The second is Parameter.

Parameter

Parameter to pass into the callback.

Return value description

TRUE if the search was started. FALSE if it failed.

 

VoiceChatAllowWaves

Call this to allow voice chat wave-bases to be used by various users.

 

Players can disguise their voices with voice chat. The disguises can include basing their disguised voice off a looped voice recording instead of their normal voice. This allows their voice to sound like a growl, or like a musical instrument, etc.

 

As a security precaution, the server only allows pre-approved wave files to be used. In order for a wave file to be pre-approved, you must pass a <VoiceChatInfo> resource into VoiceChatAllowWaves(). This will remember all the wave files from the resource.

 

 

Parameter Name

Description

AllowFiles

This is a <VoiceChatInfo>...</VoiceChatInfo> resource. See the main function description.

Return value description

If successful, this returns a list with [Time, String, Actor, True on success.

 

 

Variables (Global)

gConnectionList

Sorted list of all the connection objects on the system.

 

You can examine GConnectionList to see the list of all "Connection" objects. Do NOT change this. The list will automatically be updated as Connection objects are added or removed.

[]

gConnectionLogOffTime

Number of seconds before a user is logged off.

 

This is used when a user logs off, and dissuades them from logging out during combat.

30

gNC_Anim_Animate

Animate object (such as a person).

 

Animate object (such as a person).

 

Some languages have different noun cases for animate vs. inanimate object.

 

See NLPNounCase() for a complete description.

0x0010

gNC_Anim_Inanimate

Inanimate object (such as a box).

 

Inanimate object (such as a box).

 

Some languages have different noun cases for animate vs. inanimate object.

 

See NLPNounCase() for a complete description.

0x0020

gNC_Anim_Mask

Animated mask

 

Animated mask.

 

See NLPNounCase() for a complete description.

0x0030

gNC_Art_Definite

Definite article ("the" in English).

 

Definite article ("the" in English).

 

See NLPNounCase() for a complete description.

0x0003

gNC_Art_Indefinite

Indefinite article ("a" or "an" in English).

 

Indefinite article ("a" or "an" in English).

 

See NLPNounCase() for a complete description.

0x0002

gNC_Art_Mask

Animated mask

 

Animated mask.

 

See NLPNounCase() for a complete description.

0x0030

gNC_Art_None

Definite article ("the" in English).

 

Definite article ("the" in English).

 

See NLPNounCase() for a complete description.

0x0003

gNC_Caps_Lower

Lower-case noun. (Ex: "car", instead of "Car")

 

Lower-case noun. (Ex: "car", instead of "Car")

 

See NLPNounCase() for a complete description.

0x1000

gNC_Caps_Mask

Capitalization mask

 

Capitalization mask.

 

See NLPNounCase() for a complete description.

0x3000

gNC_Caps_Upper

Upper-case noun. (Ex: "Car", instead of "car")

 

Upper-case noun. (Ex: "Car", instead of "car")

 

See NLPNounCase() for a complete description.

0x2000

gNC_Case_Abessive

Abessive noun case.

 

Abessive noun case.

 

See NLPNounCase() for a complete description.

0x010000

gNC_Case_Ablative

Ablative noun case.

 

See NLPNounCase() for a complete description.

0x020000

gNC_Case_Absolutive

Absolutive noun case.

 

See NLPNounCase() for a complete description.

0x030000

gNC_Case_Accusative

Accusative noun case.

 

See NLPNounCase() for a complete description.

0x040000

gNC_Case_Adessive

Adessive noun case.

 

See NLPNounCase() for a complete description.

0x050000

gNC_Case_Allative

Allative noun case.

 

See NLPNounCase() for a complete description.

0x060000

gNC_Case_Comitative

Comitative noun case.

 

See NLPNounCase() for a complete description.

0x070000

gNC_Case_Dative

Dative noun case.

 

See NLPNounCase() for a complete description.

0x080000

gNC_Case_Dedative

Dedative noun case.

 

See NLPNounCase() for a complete description.

0x090000

gNC_Case_Elative

Elative noun case.

 

See NLPNounCase() for a complete description.

0x0a0000

gNC_Case_Ergative

Ergative noun case.

 

See NLPNounCase() for a complete description.

0x0b0000

gNC_Case_Essive

Essive noun case.

 

See NLPNounCase() for a complete description.

0x0c0000

gNC_Case_Genetive

Genetive noun case.

 

See NLPNounCase() for a complete description.

0x0d0000

gNC_Case_illative

Illative noun case.

 

See NLPNounCase() for a complete description.

0x0e0000

gNC_Case_inessive

Inessive noun case.

 

See NLPNounCase() for a complete description.

0x0f0000

gNC_Case_Instrumental

Instrumental noun case.

 

See NLPNounCase() for a complete description.

0x100000

gNC_Case_Locative

Locative noun case.

 

See NLPNounCase() for a complete description.

0x110000

gNC_Case_Mask

Case mask.

 

See NLPNounCase() for a complete description.

0xff0000

gNC_Case_Nominative

Nominative noun case.

 

See NLPNounCase() for a complete description.

0x120000

gNC_Case_Objective

Object noun case. English only. (Ex: "me", "him", "her")

 

NOTE: This is the same value as the accusative case.

 

See NLPNounCase() for a complete description.

0x040000

gNC_Case_Oblique

Oblique noun case.

 

See NLPNounCase() for a complete description.

0x130000

gNC_Case_Partitive

Partitive noun case.

 

See NLPNounCase() for a complete description.

0x140000

gNC_Case_Possessive

Possessive noun case.

 

See NLPNounCase() for a complete description.

0x150000

gNC_Case_Postpositional

Postpositional noun case.

 

See NLPNounCase() for a complete description.

0x160000

gNC_Case_Prepositional

Prepositional noun case.

 

See NLPNounCase() for a complete description.

0x170000

gNC_Case_Prolative

Prolative noun case.

 

See NLPNounCase() for a complete description.

0x180000

gNC_Case_Subjective

Subjective noun case. English only. (Ex: "I", "he", "she")

 

See NLPNounCase() for a complete description.

0x120000

gNC_Case_Terminative

Terminative noun case.

 

See NLPNounCase() for a complete description.

0x190000

gNC_Case_Translative

Translative noun case.

 

See NLPNounCase() for a complete description.

0x1a0000

gNC_Case_Vocative

Vocative noun case.

 

See NLPNounCase() for a complete description.

0x1b0000

gNC_Count_Few

Plural noun, but only a few ("two cars").

 

This is available since some langauges differentiate between plural (2-3) and plural (4+) for noun cases.

 

See NLPNounCase() for a complete description.

0x0008

gNC_Count_Many

Plural noun, more than a few ("ten cars").

 

This is available since some langauges differentiate between plural (2-3) and plural (4+) for noun cases.

 

See NLPNounCase() for a complete description.

0x000c

gNC_Count_Mask

Count mask.

 

See NLPNounCase() for a complete description.

0x000c

gNC_Count_Single

Singlular noun ("one car").

 

See NLPNounCase() for a complete description.

0x0004

gNC_Fam_Familiar

Familiar noun (as opposed to formal).

 

Some languages differentiate familiar nouns from formal ones.

 

See NLPNounCase() for a complete description.

0x0040

gNC_Fam_Formal

Formal noun (as opposed to familiar).

 

Some languages differentiate familiar nouns from formal ones.

 

See NLPNounCase() for a complete description.

0x0080

gNC_Fam_Mask

Familiar mask.

 

See NLPNounCase() for a complete description.

0x00c0

gNC_Gender_Female

Female gendered noun.

 

See NLPNounCase() for a complete description.

0x0800

gNC_Gender_Male

Male gendered noun.

 

See NLPNounCase() for a complete description.

0x0400

gNC_Gender_Mask

Gender mask.

 

See NLPNounCase() for a complete description.

0x0c00

gNC_Gender_Neuter

Neuter gendered noun.

 

See NLPNounCase() for a complete description.

0x0c00

gNC_Person_First

First person ("I" or "we").

 

See NLPNounCase() for a complete description.

0x4000

gNC_Person_Mask

Person (1st, 2nd, or 3rd) mask.

 

See NLPNounCase() for a complete description.

0xc000

gNC_Person_Second

Second person ("you" or "you"-plural).

 

See NLPNounCase() for a complete description.

0x8000

gNC_Person_Third

Third person ("he" or "they").

 

See NLPNounCase() for a complete description.

0xc000

gNC_Pronoun

Speaks a NLP name as a pronoun.

 

See NLPNounCase() for a complete description.

0x4000000

gNC_Quantity_Mask

Quantity mask.

 

See NLPNounCase() for a complete description.

0x3000000

gNC_Quantity_NoQuantity

Don't show any quantity numbers in front of the noun.

 

See NLPNounCase() for a complete description.

0x1000000

gNC_Quantity_Quantity

Show quantity numbers in front of the noun, such as "52 gold pieces".

 

See NLPNounCase() for a complete description.

0x2000000

gNC_Verbose_Long

Use the long form of the noun.

 

Example: The short form might be "knife" while the long form might be "long and pointy knife".

 

See NLPNounCase() for a complete description.

0x0200

gNC_Verbose_Mask

Verbose mask.

 

See NLPNounCase() for a complete description.

0x0300

gNC_Verbose_Short

Use the short form of the noun.

 

Example: The short form might be "knife" while the long form might be "long and pointy knife".

 

See NLPNounCase() for a complete description.

0x0100

gPerformanceCPU

Keeps track of the amount of CPU used for the last few minutes.

 

This is a list of CPU-performance snapshots taken every 10.0 seconds.

 

Each sub-element contains, [TimeSeconds, ProcessUsedSeconds, MainThreadTimeSeconds, MainThreadUsedSeconds]. TimeSeconds is the time, in seconds, since the process started. ProcessUsedSeconds is the number of seconds spent in the process since it began running. MainThreadTimeSeconds is the time the main thread has been running. MainThreadsUsedSeconds is the number of seconds the main thread has been used since it began running.

 

oServerTimer will keep around 100 records, covering 1000 seconds.

[]

gPerformanceNetwork

Keeps track of the network performance for the last few minutes.

 

This is a list of network-performance snapshots taken every 10.0 seconds.

 

Each sub-element contains, [TimeSeconds, Megabytes sent, Megabytes received]. TimeSeconds is the time in seconds, since the process started. Megabytes sent/received indicate how much data has been sent over the internet since the server started.

 

oServerTimer will keep around 100 records, covering 1000 seconds.

[]

gRenderCacheLimitsDataGreedy

Default value passed to RenderCacheLimits().

1000;      // 1 gig

gRenderCacheLimitsDataMaxEntriesOn32

Default value passed to RenderCacheLimits().

10000

gRenderCacheLimitsDataMaxEntriesOn64

Default value passed to RenderCacheLimits().

100000

gRenderCacheLimitsDataMaximum

Default value passed to RenderCacheLimits().

100000;    // 100 gig

gRenderCacheLimitsDataMinimum

Default value passed to RenderCacheLimits().

1000;      // 1 gig

gSafetyMaximumGUI

Maximum number of GUI objects that can be allocated to the process before it shuts down automatically.

1000

gSafetyMaximumHandles

Maximum number of handles allocated on the SERVER before automatic shutdown.

50000

gSafetyMaximumMemory

Maximum number of megabytes that can be allocated before an automatic shutdown occurs.

 

This defaults to 1.5 gigabytes, since 32-bit windows has a 2 megabyte limit.

1500; // 1.5 gb


 

gSafetyMaximumThreads

Maximum number of threads allocated on the SERVER before automatic shutdown.

2000

gSafetyMaximumDisk

Minimum amount of disk space (in gigbytes) that must be available before automatic shutdown.

 

This is a safety measure to ensure the server never runs out of disk space and crashes.

0.5; // 0.5 gigabytes

gSavedGamesSinglePlayer

Where single-player games are stored.

 

 

This is the main "file name" used for saving single-player games.

"SinglePlayer"

gShardParamIsOffline

Set by a call to ShardParamIsOffline(), and is valid thereafter.

Undefined

gTextLogDeleteOld

Automatically delete text logs older than this many days.

3

gTextLogSystem

List used to quickly determine if a system event should be logged.

 

This is a list that's used to quickly determine if a system event (not related to a specific user) should be logged. See the tutorial on the text logging features for more information.

[TRUE, TRUE, TRUE, FALSE, FALSE]

gTextLogSystemAlert

Determines what priority events should be logged.

 

gTextLogSystemAlert is used by TextLogPriorityAdjust() to determine what events are logged, and ultimately affects the list values for gTextLogSystem and gTextLogUser.

 

Use a value of 0 for a normal system alterness, causing priority 1 and 2 events to be logged, while 3 and 4 are ignored. 1 indicates a higher level of alter, with event priorities 1 through 3 being logged. 2 causes priorities 1 through 4 to be logged.

 

Changing this won't affect gTextLogSystem or gTextLogUser until TextLogPriorityAdjust() is called.

0

gTextLogUser

List used to quickly determine if a user event should be logged.

 

This is a list that's used to quickly determine if a user event (specific to a user) should be logged. See the tutorial on the text logging features for more information.

[TRUE, TRUE, TRUE, FALSE, FALSE]

 

 

Library – Basic interactive Fiction Library

BUGBUG – Not yet copied from integrated development environment. You can find this information by installing CircumReality, and running MIFServer.exe.

 

Library – Basic Administration Library

BUGBUG – Not yet copied from integrated development environment. You can find this information by installing CircumReality, and running MIFServer.exe.

 

Library – Basic communications Library

BUGBUG – Not yet copied from integrated development environment. You can find this information by installing CircumReality, and running MIFServer.exe.

 

Library – Basic RPG Library

BUGBUG – Not yet copied from integrated development environment. You can find this information by installing CircumReality, and running MIFServer.exe.

 

Library – Basic Artificial Intelligence Library

BUGBUG – Not yet copied from integrated development environment. You can find this information by installing CircumReality, and running MIFServer.exe.

 

Library – Basic fantasy Library

BUGBUG – Not yet copied from integrated development environment. You can find this information by installing CircumReality, and running MIFServer.exe.

 

Library – Basic Monsters Library

BUGBUG – Not yet copied from integrated development environment. You can find this information by installing CircumReality, and running MIFServer.exe.

 

Library – Extra Objects

BUGBUG – Not yet copied from integrated development environment. You can find this information by installing CircumReality, and running MIFServer.exe.

 

Library – Sample world

BUGBUG – Not yet copied from integrated development environment. You can find this information by installing CircumReality, and running MIFServer.exe.