RPG tutorial

From Platinum Arts Sandbox Free 3D Game Maker
Jump to: navigation, search

The following tutorials are only meant to cover the basics of developing for the RPG. Each of the tutorials build upon the work from the previous exercises and are therefor best done in order.

Part 1 - initial skeleton

This part covers making the initial skeleton of our game.


By default the RPG detects the available games by searching data/rpg/games for cfg files, these files form the basis of what the main menu will list as the available games. In selecting a game from the menu, it will then attempt to load the definitions from an equivalently named directory.

We now know the first step to making our own tutorial game, we need a corresponding cfg and directory inside data/rpg/games'

Luckily sandbox comes with a base that can be derived to quickly get things up and running, so first up, let's make a copy of the base directory and call the copy "tutorial" and then move a file named base.cfg inside data/rpg/games/tutorial into data/rpg/games and rename it to tutorial.cfg.

We can essentially open up our game in sandbox now, but we still have a few things to do, so for now, open up tutorial.cfg in your favourite text editor. The contents should be pretty self explanatory but we do need to make a few specific changes.

Firstly, we want to set the default map and associate it with mapscript 0, for this excercise we'll name our map tutorial1.

Secondly, we want to set the version number and the compatibility number to 1.

Finally, we will want to specify a HUD to use for our game. Sandbox currently only ships one, so we will execute it here.

The final version of tutorial.cfg should look similar to this

 r_preparemap tutorial1 0
 
 firstmap tutorial1
 
 gameversion 1
 compatversion 1
 
 exec data/rpg/hud_standard.cfg

Save it and we have one more step to go, we need to make a map named tutorial1, so fire up sandbox and save a newmap under that name.

Congratulations! you have successfully set up the basics required to make your RPG with sandbox

Addendum

game.cfg has two explicit purposes. Firstly it identifies the existence of a game and secondly, it defines many of the important attributes of the game.

things that can and should be defined in game.cfg include

  • the script and flags to use on a maps
  • internal properties, such as the game version
  • global game properties, such as friendly fire
  • rule properties (currently not available)
  • the initial/default HUD

I'm a windows

If windows is your operating system of choice, do yourself a huge favour and at the very least configure windows to display file extensions such as .cfg. This will save you a fair bit of pain

Part 2 - the first NPC

This section of the tutorial involves spawning a neutral NPC and assigning him some dialogue


We can go about this in a few ways, but we'll start by first defining the NPC's script, the NPC, and then creating a place for him in the world.

First off, go to your definitions directory (probably data/rpg/games/tutorial) and then enter the script subdirectory. You will not want to create a new configuration file and place it at the very end of the others (eg, if the last is named 7.cfg, name yours 8.cfg). In this file, we define how any entities using it will react and interact with the surrounding world. Since we don't want to bother with that, we will simply include the default NPC script.

Afterwards we can start defining the dialogue. When you're done the final script should look something like this. Feel free to experiment by writing more dialogue and fleshing out your character

 include scripts/1 //includes the default NPC script
 
 r_script_node "main" [result "Hello, how are you doing"] [
   r_response "I'm well, yourself?" "glad" //goes to the node glad
   r_response "Goodbye" "" //closes the dialogue
 ]
 
 r_script_node "glad" [result "I'm glad to hear it"] [
   r_response "Goodbye" ""
 ]

With that taken care of, we will now define a critter to use the script. All critter configuration options are prefixed with r_char and most contain a variant with a _get suffix for retrieving the value. Now create a new cfg file in the critter sub directory, following the same steps as you did for the script to define it's name. This will probably be 0.cfg.

Consult the configuration reference for a full list of available configuration options, for now, copy the following into critters/0.cfg

 r_char_name "Bob"
 r_char_mdl "ogre"
 r_char_script 8 //make sure this corresponds with the above script

Finally, we need to spawn Bob, so start your game and create a critter entity that spawn a critter of type 0. Remember that F1 toggles editmode in the RPG!

 newent critter 0

You're done, to test it simply exit editmode and press E while hovering the crosshair over him.


Addendum

Default Scripts

The various definition types of the RPG each default to their own individual specialized script. They correspond as follows

  • 0 - null/empty/nothing
  • 1 - Default critter script; primarily initiates dialogue, will in the future control AI logic and stealth abilities
  • 2 - Default item script; mainly implements pickup
  • 3 - Default obstacle script; does nothing
  • 4 - Default container script; will eventually allow you to pick the lock and loot items
  • 5 - Default platform script; does nothing
  • 6 - Default trigger script; toggles triggered state (eg, switches door between open and close)

Note that the player defaults to script 0, and has its script explicitly set to 7. Script 7 contains some basic player logic, mainly it lets you know when you level up.

Definition numbering

The numbers allows the game to easily and quickly identify and order definitions, unfortunately this is inconvenient to the users since it makes identifying which definition corresponds to an item you're looking for a bit of a chore. The only thing you need to remember, is that each definition must be assigned number from 0 and up and that there cannot be any empty gaps. Duplicate numbers (eg hex or octal) and non-number names will throw a warning.

$ ls
1.cfg  2.cfg  3.cfg  4.cfg  5.cfg
# the game will abort with these files


$ ls
0.cfg  1.cfg  2.cfg  3.cfg  4.cfg
# the game will start

Multiple dialogue entry points

The entry point is defined by the talk signal, by default this simply opens the dialogue at position 0 should it exist. If you'd like to change the entry point you will have to redefine the talk signal for your entity. For example...

 r_script_signal talk [
   if $cond [
     r_chat self main
   ]
     r_chat self quest
   ]
 ]

You can add more more conditions and variables and you are allowed the full range of cubescript commands.

Dialogue standards

There are no fixed standards, but we recommend the following conventions when writing dialogue for your characters.

 r_script_node "normal" [result "Things the creatures say are simply typed like this, no surrounding quotes"] []
 r_script_node "emphasis" [result "If you wish to put *emphasis* on a word, place *'s on both sides]"] []
 r_script_node "actions" [result "[between these brackets, write what your character sees and experiences]"] [
   r_response "[Likewise, place any actions your character takes here]"
 ]

Part 3 - items

This section of the tutorial covers basic item definitions, trigger basics and crafting.

For this exercise we will craft papyrus sheets, we will write on these to produce our weapon in the next part. This process demands the use of a knife, a hammer and something heavy to compress it while drying. The important part is the materials that are used in the process and those that enable the process, we recommend ignoring the process itself.

So first up, let's define the ingredients, the tools we're going to use and the product we're going to make. If you've followed the tutorial thus far, you should still have 0 item definitions, otherwise follow the previously defined rules. Save the following in the items subdirectory. The commands and their effect should be self-explanatory

0.cfg

r_item_name "Papyrus reed"
r_item_description "Freshly harvested from the Papyrus plant, the stem of these reeds have been used for centuries to create a durable writing surface."
r_item_mdl "tentus/moneybag"
r_item_value 2
r_item_weight 1
r_item_category $cat_misc

1.cfg

r_item_name "Old Knife"
r_item_description "The blade has seen a lot of use and is missing a few chips, but remains deadly sharp."
r_item_mdl "tentus/moneybag"
r_item_value 23
r_item_weight 2
r_item_category $cat_weap

2.cfg

r_item_name "Old Mallet"
r_item_description "The mallet appears to have seen a lot of use, but remains effective for beating things."
r_item_mdl "tentus/moneybag"
r_item_value 10
r_item_weight 5
r_item_category $cat_weap

3.cfg

r_item_name "Papyrus"
r_item_description "Papyrus reeds have been converted into a durable surface suitable for writing on."
r_item_mdl "tentus/moneybag"
r_item_value 4
r_item_weight 1
r_item_category $cat_misc

Next, we will define the recipe. A recipe has 3 lists of items, the ingredients, the catalysts and the products. Ingredients are items that are consumed in full to produce the products and catalysts are items that enable the process to happen.

For now, copy the following to recipes/0.cfg

r_recipe_flags $RECIPE_KNOWN //we know the recipe at the start

r_recipe_add_ingredient 0 10 // 10 x papyrus reeds

r_recipe_add_catalyst 1 1 // 1 x knife
r_recipe_add_catalyst 2 1 // 1 x hammer

r_recipe_add_product 3 1 // 1 x papyrus

We now have everything we need to create the final item, we simply need to spawn them into the world. If you are in a hurry to test it out, use the following commands to spawn them in the world.

newent item 0 10 //skip this if you aren't in a hurry
newent item 1 1 //knife
newent item 2 1 //hammer

If you're still here, we're now going to make actual harvestable papyrus plants using triggers, since we lack a suitable model we will improvise using one for reeds. First we will define a suitable script. Our trigger's script will add a random amount between 1-4 reeds to the player's inventory and then destroy itself. If we wanted, we could toggle a variable and change the model so it looks as though it was harvested and perhaps even respawn it after a few while of game time, but that's a more advanced topic for another time. So for now, copy the following into scripts/9.cfg.

 //papyrus reeds
 
 r_script_signal interact [
   // adds between 1-4 units of papyrus to whoever harvests it
   r_additem actor 0 (rnd 5 1) 
   // removes itself from the stack
   r_destroy self
 ]

As for the trigger itself, copy the following into triggers/0.cfg.

r_trigger_name "Papyrus plant"
r_trigger_mdl "dcp/reed"
r_trigger_script 9

Congratulations, just spawn a few of those near a body of water somewhere, and you've finish this section of the tutorial. The command is of course as follows

newent trigger 0

Addendum

Splitting the process

The general recommendation is to try and contain the whole process in a single recipe. Rather than splitting it out into many small pieces. As a rule, try to avoid making the player learn and execute the process and let taht be handled via the character's knowledge. For example, if we wanted to create stew in a more contemporary setting, the recommended recipe would be something like this.

Ingredients

  • 3 x Potatoes
  • 1 x Unions
  • 5 x Carrots
  • 1 x Butane canister

Catalysts

  • 1 x Knife
  • 1 x Cutting board
  • 1 x Pot
  • 1 x Portable gas cooker

Products

  • 5 x Stew

Likewise, the strongly not recommended series of recipes may look something like this

Ingredients

  • 1 x Potato

Catalysts

  • 1 x Knife
  • 1 x Cutting board

Products

  • 5 x Sliced potato


Ingredients

  • 1 x Union

Catalysts

  • 1 x Knife
  • 1 x Cutting board

Products

  • 5 x Sliced union


Ingredients

  • 1 x Sliced union

Catalysts

  • 1 x Knife
  • 1 x Cutting board

Products

  • 5 x Diced union


Ingredients

  • 1 x Carrot

Catalysts

  • 1 x Knife
  • 1 x Cutting board

Products

  • 5 x Sliced carrot


Ingredients

  • 15 x Sliced Potatoes
  • 25 x Diced Unions
  • 25 x Sliced Carrots
  • 1 x Pot

Products

  • 1 x Uncooked Stew


Ingredients

  • 1 x Uncooked Stew
  • 1 x Butane canister

Catalysts

  • 1 x Portable gas cooker

Products

  • 1 x Pot
  • 5 x Stew


Skilled Craftsmen

If you want to add a recipe that depends on skills to be used, you can use the r_recipe_reqs family of variables to set them. You're allowed to utilise the full family of stats and skills to define skill requirements for a recipe.

Part 4 - magic

This part covers covers particle effect definitions, status effect definitions and item usage definitions with the example of a weapon. The player will be able to craft it with the materials from the previous part.

Let's dive right into it. We're going to create 2 items and an additional recipe. For the first item, we need some means of writing on the papyrus we created in the previous session. The second item will be the weapon we will produce, for now we will just create a small skeleton for it. We should currently have 4 items defined, if not adjust numbers accordingly.

4.cfg

r_item_name "Ink Well"
r_item_description "It is a phial of ink. Paired with a quill these are still very popular writing instruments."
r_item_mdl "tentus/moneybag"

5.cfg

r_item_name "Magic Arrow"
r_item_description "Upon this scroll of papyrus rest incantations for a magical spell."
r_item_mdl "tentus/moneybag"

Next we will define the recipe to produce the weapon, we should currently only have a single recipe, adjust accordingly if this isn't the case.

1.cfg

r_recipe_flags $RECIPE_KNOWN

r_recipe_add_ingredient 3 1 // 1 x papyrus
r_recipe_add_ingredient 4 1 // 1 x ink

r_recipe_add_product 5 1 // magic arrow scroll

We now have to deal with the most important part to make any weapons, swords, spells or otherwise. We need a status group to do something. Zero of more status effects comprise each group, and this design decision mainly exists to accommodate spells like polymorph and their respective dispelling. The statuses sub directory should already contain a dummy group, once again, adjust the numbers as required for our own.

1.cfg

//heavy damage
r_status_friendly 0 //this is decidedly hostile
r_status_addgeneric $STATUS_HEALTH -100 0 // instantly remove 100 HP

We currently have everything we need to define our weapon/spell, but we unfortunately won't be able to see the projectile at present due to having no particle effects defined. Projectiles can have up to 3 effects, that is the projectile itself, its trail and the explosion at the end. -1 can be substituted in any of the effect slots to disable emissions for that part. We will define the following 2 effects in the effects subdirectory.

0.cfg

// flying arrow
r_effect_mdl "hirato/arrow"
r_effect_flags 0

1.cfg

// purple magic sparkles
r_effect_flags 0
r_effect_particle $PART_STEAM
r_effect_colour 0x3F003F
r_effect_size  0.5

We now have everything we need to define our weapon fully as well as be able to see any of its attacks. To make the weapon usable, we need to define a use case in which it is a weapon. We have 3 possible use cases, use (eg, read, drink), armour and weapon, the last two are implemented and fully functional while the first isn't. Naturally we want the weapon use case.

So to define our weapon fully, open up items/5.cfg and add the following at the end. Generally speaking you don't need to define quite as many properties, most of these are here just to be sure things work as expected.

r_item_use_new_weapon // 0 - it's good practice to number these entries

//these slots are available in consume and armour use cases as well
r_item_use_name "Magic Arrow"
r_item_use_description "An arrow is conjoured from the caster's finger tip and flung at its target with sheer mental will."
r_item_use_cooldown 1500
//increases the strength of the spell with player skill or charging
r_item_use_chargeflags $CHARGE_MAG
// adds our status effect at 30% efficiency - with the above charge flag that means 30 damge 
// its type is also "magic" which means its efficiency is reduced by magic resisting items.
r_item_use_new_status 1 $ATTACK_MAGIC 0.3

//these slots are available with armour use cases as well
r_item_use_slots $SLOT_LHAND
r_item_use_skill $SKILL_MAGIC

//these slots are exclusive to weapons
r_item_use_pflags $P_TIME
r_item_use_angle 0
r_item_use_lifetime 1000
r_item_use_gravity 0
r_item_use_projeffect 0 //substitute with your arrow's effect
r_item_use_traileffect 1 //substitute with your trail's effect
r_item_use_deatheffect -1 //replace with a suitable EoL effect
r_item_use_ammo -1 //use mana
r_item_use_cost 15 //uses 15 units of mana
r_item_use_kickback 10
r_item_use_recoil 10
r_item_use_target $T_SINGLE
r_item_use_speed 1.5 // in 100's of cubes a second

Congratulations! Just spawn the ink well somewhere and craft your weapon, and have some fun. In the next part we will make several mobs for you to use it against.

Addendum

Reserved ammo types

Specifying ammo 0 and above for a use case means you're going to use items from the respective ammo group. In addition there are also 3 additional ammo types that occupy numbers -1, -2 and -3. they are as follows.

  • -1 - Mana
  • -2 - Health
  • -3 - Experience

$STATUS_HEALTH, $RECIPE_KNOWN, what is all this stuff!?

These are hard coded RPG constants that are reproduced inside data/rpg/game.cfg and are usually kept in sync. We strongly recommend that you use these constants/variables as opposed to using the numbers directly, this helps ensure future compatibility and makes it easier to understand the intended effect of the definitions if updates are required.

The file contains all of the various flags and numbers that are available in the RPG, so being familiar with them will be a great boon to you if you choose to use sandbox.

Part 5 - monsters

This section of the tutorial covers debug settings, waypoints, and basic AI by making a hostile mob for the player to defeat. Before we even consider adding any AI or monsters, we need to canvas our map with waypoints for the AI to follow and get to the player.

recommended layout for the first tutorial map

But before we get to the waypoints, there is a more critical item to consider, we need to shelter the beast first, so that it doesn't attack the player immediately, specifically so that the player has a chance to build his weapon before taking on the beast. I'd suggest erecting a wall behind Bob's house, see the screenshot on the right for a recommended layout.

Our second order of business is to then enable the AI debug channel so that we can see the waypoints we're dropping and then to canvas the area. Considering the terrain will be mostly flat, you may wish to turn on the experimental waypointing method as well, it will speed up the process tremendously. With that said, waypointing is by far the most tedious task to do sufficiently well in the RPG.

debug 32 //ai debug channel
dropwaypoints 1 //required to drop waypoints
experimentalwaypoint 1 //method that links to all nearby nodes, as opposed to tracing a path
savewaypoints // saves them when you're done DO NOT FORGET TO DO THIS!!!

Our first order of business is to make a faction for our mob, at present this is only useful for querying relationships between factions as well as allowing you to hurt each other with friendly fire protection enabled. You may wish to put Bob inside his own faction as well. Copy the following as the definition for the second faction.

1.cfg

r_faction_name "Beasts"
r_faction_base 0

We will now define a script for our creature, we want him to attack us if he spots us as well as when we bother or attack him. The ai for now is very basic and will not try to dodge or do anything besides rush in and complete its goal (which is, to kill you). The following will be his script for now.

10.cfg

//our mob's script
include scripts/1

r_script_signal talk [
  if (!= (r_get_faction self) (r_get_faction actor)) [
    r_action_clear self
    r_action_attack self actor
  ]
]

r_script_signal hit [
  if (!= (r_get_faction self) (r_get_faction actor)) [
    r_action_clear self
    r_action_attack self actor
  ]
]

r_script_signal update [
  if (r_cansee self player) [
    r_action_clear self
    r_action_attack self player 1
  ]
]

r_script_signal collide [
  if (!= (r_get_faction self) (r_get_faction actor)) [
    r_action_clear self
    r_action_attack self actor
  ]
]

We can now define our beast, let's make him a hungry and aggressive wolf

r_char_name "Wolf"
r_char_mdl "rpg/characters/wolf"
r_char_script 10
r_char_faction 1

r_char_base_maxspeed 40 //speed boost
r_char_base_jumpvel 80

We've now done all we need to spawn him, but he won't pose any threat to us at present. For that we need to define a weapon for him and equip it. Since he's a wolf, he would probably use his fangs for a short range low damage attack with little cooldown. So let's define his fangs in the items directory with those properties.

6.cfg

r_item_name "Fangs"
r_item_description "If you see this, submit a bug report."

r_item_use_new_weapon // 0
r_item_use_cooldown 100
r_item_use_chargeflags $CHARGE_MAG
r_item_use_new_status 1 $ATTACK_SLASH .075

r_item_use_slots (| $SLOT_LHAND $SLOT_RHAND)
r_item_use_skill $SKILL_MELEE

r_item_use_range 4
r_item_use_angle 8 //degrees
r_item_use_lifetime 0
r_item_use_cost 0
r_item_use_target $T_HORIZ
r_item_use_recoil -10
r_item_use_projeffect -1
r_item_use_traileffect -1
r_item_use_deatheffect -1

With that taken care of, we now need to spawn him with the item and equip them. In spite of how wolves normally attack, this will be equipped on his "paws". So open up his script and append the following onto the end. Also, we will place a location entity somewhere in the map near the wolf, he will wander around near it until he spots the player.

10.cfg

r_script_signal spawn [
  r_action_wander self 0 96 0
  r_additem self 6 1
  r_equip self 6 0
]

Addendum

Debug flags

The debug variable is a set of flags, you can toggle most of them from the options menu under the game tab, but here they are, reproduced in full.

  • 1 - World - draws misc information on the HUD and prints some information on the map stack
  • 2 - Entity - draws entity pointers above head and prints when more are spawned and destroyed as well as their deaths and a few other things
  • 4 - Configuration - prints what various properties were set to when loading definitions
  • 8 - Projectiles - prints hit testing details and renders a pointer string over the projectile
  • 16 - Script - informs you of most executed commands and any sent and received signals (when verbose)
  • 32 - AI - draws waypoints and pritns any received commands
  • 64 - I/O mainly lets you know what is being read from savegames
  • 128 - Camera, draws cutscene information on the HUD
  • 256 - Verbose, makes the rest spammier

Particles are everywhere!

If you're being overwhelmed by particles from rendering the waypoints, whether that involves them flickering, or simply bringing performance to a crawl, you can use maxparticledistance to adjust the draw distance, just don't forget to take note of its original value and change it back afterwards

Part 6 - a simple quest

This section of the tutorial involves creating a fetch quest, mapflags and teleports

suggested layout for dungeon level

First up, before we get to the scripts, we are going to create an additional map set below tutorial1, the name of this will be tutorial2. If you are so inclined you can simply create the required geometry below the map, but this is to introduce you to mapflags and mutliple map setups.

We need at least 2 medium sized rooms, connected to each other, with the cavern on the first tutorial map leading into one of them. The first room will be the subject for part 7, the second will house the mcGuffin we will create for this part. The screenshot on the right has an example layout.

After that is done, our first order of business would be to add this to the list of maps for the tutorial game. Since we plan to create a special HUD for the map in the next part, it would be easiest for us if we can prohibit saving. Add the map as follows below the first map.

r_preparemap tutorial2 0 $MAP_NOSAVE

This now brings us to the most important, we need to define some means of getting in and out of the dungeon. We will need two triggers and scripts to do this. We will be using a special trigger flag that will make the trigger invisible and we will be using the collide script slot to trigger area transitions. If you like me, dislike that, use the interact slots instead. Spend some time first finding a model that fits the entrance to your cavern/dungeon well and would guarantee a collision before continuing. First off, let's define the scripts for our triggers.

11.cfg

r_script_signal collide [
  r_teleport actor 0 tutorial2
]

12.cfg

r_script_signal collide [
  r_teleport actor 0 tutorial1
]

The scripts would literally move whatever touches it to the specified map at teledest 0, but more on that later. We will now define our triggers.

1.cfg

r_trigger_name "Enter cavern"
r_trigger_script 11
r_trigger_mdl "ahab/castle_door"
r_trigger_flags $TRIG_INVIS

2.cfg

r_trigger_name "Leave cavern"
r_trigger_script 12
r_trigger_mdl "ahab/castle_door"
r_trigger_flags $TRIG_INVIS

With that done, you can now spawn the triggers at their respective places. After doing that, spawn teledest ents with a tag of 0 outside the immediate area, so that the teleport command has a destination to place anything it teleports.

Our next step will be to make the mcGuffin, the mcGuffin in this case will be an item.

7.cfg

r_item_name "McGuffin"
r_item_description "This item is needed to advance the pl- er.. tutorial."
r_item_mdl "hirato/box/standard"

You will need to spawn this in the back of the dedicated room in the dungeon. After that is done, we only need to edit 2 more files, first up, open up variables,cfg.

You use this file to store variables and values you want to persist across sessions. So you want to use these to record the various choices the player has made, the things he has done and the status of all kinds of things in the world.

append this onto the end of variables.cfg

bobmcguffin = (r_global_new 0) //bob's quest to retrieve the mcguffin, 1 - have quest 2 - finished quest

We now only have Bob's dialogue left to go. We will provide different responses for the various states as well as set any variables that need be and remove any inventory items that need be.

We will need to change a fair bit of his script, So without further ado, open up his script and replace the dialogue with the following.

8.cfg - the following code parts are all out of the same cfg, they are just separated for better readability

r_script_node "main" [result "Hello, how are you doing"] [
  r_response "I'm well, yourself?" "howareyou" //goes to the node howareyou
  case (r_global_get $bobmcguffin) 0 [
    r_response "Do you have any work for me?" "work"
  ] 1 [
    if (r_get_amount player 7) [
      r_response "I brought you the mcGuffin" "noreward"
    ] [
      r_response "I brought you the mcGuffin" "missing"
    ]
  ] 2 [
    r_response "Do you have any more work for me?" "nowork"
  ]
  r_response "Goodbye" "" //closes the dialogue
]


r_script_node "howareyou" [result "I'm glad to hear it" [
  r_response "Goodbye" ""
]


r_script_node "work" [result "I need someone to go into the cave to the North-West of this valley and retrieve the mcGuffin, can you do that for me?" [
  r_response "You can count on me!" "wait" [ r_global_set $bobmcguffin 1 ]
  r_response "Maybe some other time" ""
]

r_script_node "wait" [result "Great! I'll be waiting for your return" [
  r_response "[End]" ""
]

r_script_node "noreward" [result "Thank you for finding it, but I don't have a reward for you but I'd till very much like the mcGuffin." [
  r_response "[Give him the mcGuffin]" "thankyou" [
    r_remove player 7 1 //remove the mcGuffin out of players inventory
    r_givexp player 900 //give player some exp in return
    r_global_set $bobmcguffin 2
  ]
  r_response "I think I'll hold onto it for now" ""
]

r_script_node "thankyou" [result "Thank you very much, I will never forget this" [
  r_response "[End]" ""
]

r_script_node "missing" [result "Where is it?" [
  r_response "I must've forgotten it in the cave..." ""
]

r_script_node "nowork" [result "No, but once again, thank you for getting this for me" [
  r_response "Farewell" ""
]

Congrats, you've made your first FedEx quest. I hope you're ashamed of yourself and will take this moment to reflect on your actions and produce quests with multiple paths and solutions that aren't simply get X of Y or kill X of Y ;)

Addendum

Mapflags

There are a variety of mapflags that will affect your ability to do things on the various maps, at present they are as follows

  • F_VOLATILE - clears the map state when the player leaves, useful for dungeon levels
  • F_NOSAVE - prevents saving whilst on the map - use sparingly
  • F_NOMINIMAP - doesn't generate and display a minimap while this flag is active

Global Variable Tips

These are saved with your game and remain in a constant order and are all defined inside variables.cfg and they can only be integers. r_global_new returns the variable's index (ie, the first will return 0) and you should alias these return values to an easy to remember name that is short and as descriptive as possible. If you need to list more detailed information, do so in comments either before or on the same line.

You will want to increase the game/compat versions when you move/remove/add anything in here, serious breakage can occur

You should keep the names short and easy to type, for example, if we have a quest where you find a someone named Jane a bunch of apples, JaneApples and ApplesForJane would be some of the better names.

Journal The journal is to record rumours, quest notes, deeds, obituaries and all kinds of other things.

r_journal_record "mcGuffin" "I shall go into the cave to the North-West of this valley and retrieve the mcGuffin." This will add to the entry mcGuffin in your journal the message "I shall go into..".

Part 7 - a basic puzzle

This part of the tutorial involves the creation of a basic logic puzzle to open a door.

We will create a 10 digit number pad of sorts, the player would need to interact with in a specific order to remove an obstacle we will place in the second area.

Our first order of business will be to define the variables we'll need to pull this off, we'll need two for the method we'll be using.

variables.cfg

keyprogress = (r_global_new 0)
keytest = (r_global_new -1)

Next we'll define the button's scripts. The scripts will set the keytest variable and will then call the "testkey" signal on the current map and propagate it to the door. We will define the door a bit later.

13.cfg

r_script_signal interact [
  r_global_set $keytest 0
  r_signal "testkey" self curmap 1
]

14.cfg

r_script_signal interact [
  r_global_set $keytest 1
  r_signal "testkey" self curmap 1
]

15.cfg

r_script_signal interact [
  r_global_set $keytest 2
  r_signal "testkey" self curmap 1
]

16.cfg

r_script_signal interact [
  r_global_set $keytest 3
  r_signal "testkey" self curmap 1
]

17.cfg

r_script_signal interact [
  r_global_set $keytest 4
  r_signal "testkey" self curmap 1
]

18.cfg

r_script_signal interact [
  r_global_set $keytest 5
  r_signal "testkey" self curmap 1
]

19.cfg

r_script_signal interact [
  r_global_set $keytest 6
  r_signal "testkey" self curmap 1
]

20.cfg

r_script_signal interact [
  r_global_set $keytest 7
  r_signal "testkey" self curmap 1
]

21.cfg

r_script_signal interact [
  r_global_set $keytest 8
  r_signal "testkey" self curmap 1
]

22.cfg

r_script_signal interact [
  r_global_set $keytest 9
  r_signal "testkey" self curmap 1
]

If you thought that wasn't boring enough, we now define the buttons themselves with similarly minor alterations, these should occupy slots 3 to 12 in the triggers directory, feel free to use a more appropriate model if you have one available.

3.cfg

r_trigger_name "0 Key
r_trigger_mdl "teleporter
r_trigger_script 13

4.cfg

r_trigger_name "1 Key
r_trigger_mdl "teleporter
r_trigger_script 14

5.cfg

r_trigger_name "2 Key
r_trigger_mdl "teleporter
r_trigger_script 15

6.cfg

r_trigger_name "3 Key
r_trigger_mdl "teleporter
r_trigger_script 16

7.cfg

r_trigger_name "4 Key
r_trigger_mdl "teleporter
r_trigger_script 17

8.cfg

r_trigger_name "5 Key
r_trigger_mdl "teleporter
r_trigger_script 18

9.cfg

r_trigger_name "6 Key
r_trigger_mdl "teleporter
r_trigger_script 19

10.cfg

r_trigger_name "7 Key
r_trigger_mdl "teleporter
r_trigger_script 20

11.cfg

r_trigger_name "8 Key
r_trigger_mdl "teleporter
r_trigger_script 21


12.cfg

r_trigger_name "9 Key
r_trigger_mdl "teleporter
r_trigger_script 22

We will not get to the fun part! defining the actual door itself (hooray!). Like in every other instance, we will define the script first. The door's script will be as follows, it need to test and increment the global variables we've defined earlier.

23.cfg

key = [ 3 1 3 3 7 ] //change as appropriate
keylen = (listlen $key)

r_script_signal testkey [
  if (< (r_global_get $keyprogress) @keylen) [
    if (= (at [@@@key] (r_global_get $keyprogress)) (r_global_get $keytest) [
      r_global_set $keyprogress (+ (r_global_get $keyprogress) 1)
    ] [
      r_global_set $keyprogress 0
    ]
    if (= (r_global_get $keyprogress) @@keylen) [
      r_trigger self
    ]
  ]
]

We will now make the trigger itself, like with the last step, choose a model of appropriate size to block the passage to the mcGuffin

13.cfg

r_trigger_name "Heavy door"
r_trigger_mdl "ahab/castle_door"
r_trigger_script 23

Addendum

a guiding hand

Depending on the puzzle and its difficulty, you should strive to give the player a few hints to solve it. You should avoid giving the player the answer but you should give the player some audio or visual feedback based on his actions.

Take our above puzzle for example, we have no idea if the combination we have entered is even correct apart from the door opening after interacting with the triggers. In addition, trying every 5 digit combo to get the example combination can take as many as 100000 attempts.

For a puzzle like that, it is unreasonable to just expect the player to figure out the answer himself without a few hints. Short of giving the player the answer, we could perhaps tell him the digits but in a wrong order, or perhaps give him a mnemonic he has to decipher to get the answer.

Extras

<Broken Link Removed>