summaryrefslogtreecommitdiff
path: root/lib/mods/theme/help/lua_ques.txt
diff options
context:
space:
mode:
Diffstat (limited to 'lib/mods/theme/help/lua_ques.txt')
-rw-r--r--lib/mods/theme/help/lua_ques.txt299
1 files changed, 299 insertions, 0 deletions
diff --git a/lib/mods/theme/help/lua_ques.txt b/lib/mods/theme/help/lua_ques.txt
new file mode 100644
index 00000000..1d4b9c65
--- /dev/null
+++ b/lib/mods/theme/help/lua_ques.txt
@@ -0,0 +1,299 @@
+|||||oy
+#####R /----------------------------------------\
+#####R < Adding a new quest >
+#####R \----------------------------------------/
+
+#####R=== Introduction ===
+
+Adding a quest involves a bit more work, and there is, in some ways, rather
+more potential for things to go wrong! But it's a great way of showing just
+WHAT can be done with lua scripting. It proves just how much a lua patch can
+change the overall feel of the game. And it will give you a much better idea of
+how lua interfaces with the game source. You should have read the
+*****lua_intr.txt*0[scripting introduction], *****lua_pow.txt*0[racial power tutorial]
+and *****lua_skil.txt*0[adding new skills tutorial] before going much
+further. All of the above files contain some fairly fundamental information
+which you will find necessary for full understanding of this file.
+
+The script we're looking at is going to create a quest entrance in the middle
+of Bree. Entering the quest you see a little girl who has had her necklace
+stolen. Your job is to travel down a corridor, killing some monsters on the
+way, pick up the amulet and return it to the girl. Once done, she'll reveal the
+stairs back to Bree, and give you a (randomly generated) ring. If you feel the
+monsters are too hard, the only thing to do is talk to the little girl who will
+reveal the stairs again, failing the quest for you, and also block off the
+entrance to the amulet so that you can't cheat and make off with the amulet!
+
+#####R=== Getting started ===
+
+Open amulet.lua (you have downloaded the example scripts from
+[[[[[Ghttp://www.moppy.co.uk/angband.htm], haven't you?). The first thing you
+should see is that yet again we're calling a function that takes its arguments
+from a table, making it easy to read what's going on in the script.
+
+This time our function is add_quest and we have the following keys and values:
+
+#####B["global"] = "AMULET_QUEST",
+#####B["name"] = "Hannahs lost amulet",
+#####B["desc"] = {
+#####B "Retrieve an amulet for Hannah Cooke. It's guarded!"
+#####B },
+#####B["level"] = 5,
+#####B["hooks"] = {
+
+[[[[[B"global" = ] is a constant that we set when we refer to this quest in
+various places...
+[[[[[B"name" = ] Obviously a long name for the quest. This will appear in the
+quest screen (Ctrl-Q) and we may use in some map files too.
+[[[[[B"desc" = ] This is a long description for the quest. Again this is what
+will appear in the quest screen, and each line should be not more than 80
+characters.
+[[[[[B"level" = ] This is a rough indicator of how hard the quest is, and again,
+appears in the quest screen
+[[[[[B"hooks" = ] This is the real 'meat' of the quest. Like the [[[[[B"spell_list"] key
+in the [[[[[Badd_magic] function, this is another sub-table.
+
+To understand fully the structure of the "hooks" key it's worth taking a bit of
+a detour at this point and discussing how the scripting interface works in
+general.
+
+#####R=== How the scripts work (ish) ===
+
+Essentially there's a list of events that happen in the game. As each of these
+events happen they run a check to see if there's any functions that they need
+to run at that event. When we ran the add_mkey part of adding a new skill
+power, we essentially said "when the 'm' key is pressed in the game, perform
+the [[[[[Bexecute_magic(constructor_powers)] function". Likewise we did a similar
+thing with adding the racial power, only we hooked onto the pressing of the
+'U' key.
+
+All of this was partly hidden because of the way that the [[[[[Badd_magic] and
+[[[[[Badd_power] functions work. But here in the [[[[[Badd_quest] function it's a bit more
+specific. We are going to specify what events we're going to hook onto, and
+what functions we want to trigger at that event.
+
+A full list of hooks can be found in the source-file util.pkg.
+
+#####R=== The hooks ===
+
+#####B[HOOK_BIRTH_OBJECTS] = function()
+#####B quest(AMULET_QUEST).status = QUEST_STATUS_TAKEN
+#####Bend,
+
+So here we are with our first hook. We've declared that we're adding it to the
+birth of your character. That is, the function will be called when you create
+your character. And what we're doing here is automatically declaring the quest
+as being taken, like the Dol Guldur quest is. Each quest has 7 different
+statuses:
+
+[[[[[BQUEST_STATUS_IGNORED -1 ] This is unused, but the quest is
+ignored (will not be taken and has not been taken).
+[[[[[BQUEST_STATUS_UNTAKEN 0 ] The quest has not been accepted yet
+[[[[[BQUEST_STATUS_TAKEN 1 ] You have accepted the quest
+[[[[[BQUEST_STATUS_COMPLETED 2 ] You have completed the quest
+successfully but not been rewarded for it
+[[[[[BQUEST_STATUS_REWARDED 3 ] You've completed and rewarded the quest
+[[[[[BQUEST_STATUS_FAILED 4 ] You've failed the quest
+[[[[[BQUEST_STATUS_FINISHED 5 ] The quest is completely finished
+successfully.
+[[[[[BQUEST_STATUS_FAILED_DONE 6 ] The quest is completely finished
+unsuccessfully.
+
+You see that we've used the constant we defined in the "global" section is
+passed as an argument to [[[[[Bquest.status].
+
+Next hook then:
+
+#####B[HOOK_GEN_QUEST] = function()
+#####B if (player.inside_quest ~= AMULET_QUEST) then
+#####B return FALSE
+#####B else
+#####B load_map("amulet.map", 2, 2)
+#####B return TRUE
+#####B end
+#####Bend,
+
+Ok, we're hooking onto the generation of the quest here. This is specifically
+triggered in this instance by going down the quest entrance stairs in Bree.
+Once you've gone down the stairs, you are technically inside the quest, which
+means we can say if the person is not inside the amulet quest, then ignore this
+function, otherwise load the file 'amulet.map' at co-ordinates x=2 y=2. You'll
+find the amulet.map file in the edit directory, make sure you check it out. The
+syntax for map files is fairly simple, though I might get round to writing a
+tutorial on them some day! In the mean time holler for me at the usual email
+address if you're unsure.
+
+#####B[HOOK_FEELING] = function()
+#####B if (player.inside_quest ~= AMULET_QUEST) then
+#####B return FALSE
+#####B else
+#####B cmsg_print(TERM_L_BLUE, "Hannah speaks to you:")
+#####B cmsg_print(TERM_YELLOW, "'Some nasty monsters stole my
+#####B favourite necklace.'")
+#####B cmsg_print(TERM_YELLOW, "'It's hidden at the back of that
+#####B corridor! Please fetch it for me'")
+#####B return TRUE
+#####B end
+#####Bend,
+
+We're moving into some rather more obvious territory here, and getting into the
+meat of the quest. The [[[[[BHOOK_FEELING] is triggered at the point when the level
+feeling appears. It's important that this is run only if the player is inside
+the amulet quest, as otherwise it will trigger EVERY time a level feeling
+occurs, when you go down a level in the barrow-downs, whenever! Returning TRUE
+will replace the level feeling with what's above, returning FALSE will still
+perform the function but will amend the normal level feeling - so here if we'd
+returned false we'd still get our custom messages, but they'd follow with
+'looks like a typical quest level'. Of course returning false may cause you
+other problems (see end of this file!) depending on what else you have in your
+function.
+
+#####B[HOOK_GIVE] = function(m_idx, item)
+
+#####B m_ptr = monster(m_idx)
+#####B o_ptr = get_object(item)
+
+#####B if (m_ptr.r_idx == test_monster_name("Hannah Cooke, a little girl"))
+#####B and (o_ptr.tval == TV_AMULET) and (o_ptr.sval == 2) then
+
+#####B cmsg_print(TERM_YELLOW, "'Thank-you!'")
+
+#####B inven_item_increase(item, -1)
+#####B inven_item_optimize(item)
+
+#####B quest(AMULET_QUEST).status = QUEST_STATUS_COMPLETED
+
+#####B cave_set_feat(7, 6, 6)
+
+#####B cmsg_print(TERM_YELLOW, "'Here, take this pretty ring I found
+#####B as a token of gratitude!'")
+#####B random_type = randint(57)
+#####B reward = create_object(TV_RING, random_type)
+#####B drop_near(reward, -1, py, px)
+#####B quest(AMULET_QUEST).status = QUEST_STATUS_REWARDED
+#####B return TRUE
+#####B else
+#####B return FALSE
+#####B end
+#####Bend,
+
+This is a fairly long function, but don't be intimidated. It's not really
+difficult to understand. As you can see we're hooking into the giving of an
+object to a monster (the 'y' key). Because of this, the function takes two
+arguments - [[[[[Bm_idx] (the monster that you're giving to) and [[[[[Bitem] (the item that
+you're giving).
+
+We then make it possible to work with the monster and item variables by
+referencing them to two functions which identify them from the edit files:
+[[[[[Bmonster()] and [[[[[Bget_object()]. This enables us to now say, 'if the name of the
+monster is "Hannah Cooke, a little girl" and the type of item is an amulet and
+that amulet is an amulet of adornment, then carry out the following commands'.
+
+We then say call the function [[[[[Binven_item_increase()] which places an object in
+the inventory. It takes two arguments, the first being what object to put in
+the inventory and the second being how many of that type of objects to put in
+the inventory. You can see that by placing -1 as the second argument it fairly
+obviously subtracts that item from the inventory. The [[[[[Binven_item_optimize()]
+function checks that there are no empty inventory slots, and if there are,
+erases them.
+
+The quest is then completed, and the stairs are revealed using the
+[[[[[Bcave_set_feat()] function. This function takes three arguments, the first is the
+x co-ordinate of the cave square you wish to change (counted from top left) the
+second is the y co-ordinate, and the third is the index number of the feature
+you wish the square to become as defined in f_info.txt.
+
+We then set about rewarding the player. As you can see we call [[[[[Bcreate_object()]
+which takes two variables: the first is the type of object (these are all
+listed in object.pkg) and the second is the sub-type of that object. I searched
+k_info.txt to see how many different types of ring there were (57) and used a
+randomly selected number with a maximum value of 57 as that specific sub-type.
+
+We then drop the object (although it's been created, it has only been created
+in the game's memory, it's nowhere that the player can interact with it until
+we drop it). The [[[[[Bdrop_near()] function takes 3 variables, the first being the
+object that you wish to drop, the second being the chance that it disappears
+(like an arrow, or mimicked creature) on drop. If you set it to -1, it won't
+ever disappear. The last two are the co-ordinates at which the object will be
+dropped. py and px are the global variables defined by where the player is
+standing, so in this case it will drop under the player. You could do
+[[[[[Binven_item_increase(reward, 1)] if you wanted, but I wanted to show a variety of
+ways of handling objects.
+
+OK, let's take a look at the next hook:
+
+#####B[HOOK_CHAT] = function(m_idx)
+#####B m_ptr = monster(m_idx)
+#####B if (m_ptr.r_idx == test_monster_name("Hannah Cooke, a little girl")) then
+#####B if (quest(AMULET_QUEST).status == QUEST_STATUS_REWARDED) then
+#####B cmsg_print(TERM_YELLOW, "'Bye!'")
+#####B else
+#####B cmsg_print(TERM_YELLOW, "'Are the monsters too tough?
+#####B Do you want to leave?'")
+#####B if (get_check("Really leave and fail the quest?") ==
+#####B FALSE)
+#####B then
+#####B cmsg_print(TERM_YELLOW, "'Go and get my
+#####B amulet then!'")
+#####B else
+#####B cmsg_print(TERM_YELLOW, "'Awww. Never
+#####B mind. It was only a bit of rabbits foot'")
+#####B quest(AMULET_QUEST).status =
+#####B QUEST_STATUS_FAILED
+#####B cave_set_feat(7, 6, 6)
+#####B cave_set_feat(12, 5, 60)
+#####B end
+#####B end
+#####B return TRUE
+#####B end
+#####B return FALSE
+#####Bend,
+
+This only looks complicated because of the nested 'if' statements. It's easy to
+lose your way when doing this kind of thing, always make sure you close all the
+statements and put the returns in the right place. [[[[[BHOOK_CHAT] functions have one
+argument - the monster you are chatting to. As you can see, we perform a check
+to make sure it's the right monster and then away we go.... If the player wants
+to leave the quest without completion they talk to Hannah, who gives them a
+chance to change their mind! If the player asks to leave the entrance to the
+corridor is blocked off (the second cave_set_feat()) so that the user can't
+then go and get the amulet. Gumband or Zangband players may at this point think
+they've lost out on the rabbits foot of burglary! (they haven't though as it
+doesn't exist in ToME).
+
+#####B[HOOK_CHAR_DUMP] = function()
+#####B if (quest(AMULET_QUEST).status == QUEST_STATUS_FAILED) then
+#####B print_hook("\n You chickened out of rescuing a necklace and
+#####B made a little girl sad. ")
+#####B elseif (quest(AMULET_QUEST).status == QUEST_STATUS_COMPLETED) or
+#####B (quest(AMULET_QUEST).status == QUEST_STATUS_REWARDED) or
+#####B (quest(AMULET_QUEST).status == QUEST_STATUS_FINISHED) then
+#####B print_hook("\n You rescued little Hannah Cooke's necklace from
+#####B the nasty monsters ")
+#####B end
+#####B return FALSE
+#####Bend,
+
+This quite simply and obviously prints an appropriate line in the character
+dump based on the status of the quest. The [[[[[B\n] bit ensures the text goes on a
+new line, so make sure you include it! Also you should return FALSE as
+returning TRUE will stop executing all the other character dump lines (and you
+may get other quests not having their lines printed).
+
+=== A word about returning TRUE and FALSE ===
+
+As I mentioned above, you need to be careful what you return when dealing with
+HOOKS as you can mess up the game a bit. Bear in mind that if you add a
+function to [[[[[BHOOK_GEN_QUEST], every time a quest is generated, that function will
+run. If you return TRUE, then no further functions attached to that hook will
+run. If you return FALSE, it continues processing functions on that hook.
+
+That is pretty much it. Do take a look at the other included scripts that I
+haven't gone into any detail about in the files, as you'll pick up some useful
+techniques there too. Especially worthy of note is the hina.lua file which uses
+hooks outside of the quest structure and also global variables and variables in
+a table. If you have any questions, let me know at the email addy below.
+
+Back to the *****lua.hlp*0[lua help index] .
+
+ [[[[[gThis file by fearoffours (fearoffours@moppy.co.uk)]