Help with quests
by CosmicGerbil
This help is not very good, because I am a novice at lua programming. It guides you through making a simple quest, but does not explain the
syntax of the lua programming language, because I don't yet understand it that well. What I do know I have learnt from help from Falcon.
Thanks Falcon :)
What is a quest?
In some towns, if you go into certain buildings you will be offered quests. These can involve:
Killing a certain unique monster
Killing a set number of monsters
Rescuing a friendly monster
Finding a certain object
If you complete the quest successfully, you will be given a reward.
This example shows a simple quest where you have to kill one unique monster. The quest is split into 3 parts.
The first part loads the map.
--Wererat quest
The two "--" signs indicate that this line is just a comment and shows the name of the quest.
wererat_quest = {}
I'm not sure what this line does yet, but it's important :)
wererat_quest.MAP =
[[#!map
These two lines start loading the map. It is important when designing a map for a quest level to put
the quest name in lower case letters. I will explain more about this further on in the help.
# Monsters start awake
N:0
The comment above explains this line. The monsters are always awake and alert to you.
# Permanent wall
F:X:63:3
# Granite Wall
F:#:57:3
# Floor
F:.:1:3
# Dirt
F:-:88:3
# Giant white rat
F:a:88:3:49
# Giant black rat
F:b:88:3:61
# wererat
F:c:88:3:108
# Chief wererat
F:r:88:3:405
# Quest exit
F:<:6:3
These lines simply add in quest features (walls, floor etc) and monsters. The Chief wererat is the unique monster you must kill to complete the quest. I
will explain more about him further down.
D:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
D:X---#------------------------------X
D:X---#-------a--------------b-------X
D:X----------------------------------X
D:X##############-###################X
D:X----------------------------------X
D:X--------------------c-------------X
D:X----------------------------------X
D:X###########################-######X
D:X----------------------------------X
D:X---------#-------c----------------X
D:XXXXXXX---#--------------c---------X
D:X--<--X---#------------------------X
D:X------r--#------------------------X
D:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
The actual map is here :) Maps are simply drawn into the script, like this map here.
# Starting position
P:2:3
]]
The top line is a comment. The second line down shows where the player starts when he enters the quest, and the
brackets on the bottom line close the map part of the quest.
The second part of the script features the quest data and the hooks. The quest data is the info that is printed on the
quest screen when the player has taken the quest, and when the quest is completed, but the player has not yet claimed their
reward. When playing FuryBand, you can view the quest screen by pressing ctrl and Q.
-- Quest data and hooks
add_quest
{
["global"] = "WERERAT_QUEST",
["name"] = "Wererat quest",
["desc"] = function()
-- Quest taken
if (quest(WERERAT_QUEST).status == QUEST_STATUS_TAKEN) then
print_hook("#####yWererat Quest! (Danger Level: 10)\n")
print_hook("Kill the chief wererat.\n")
print_hook("\n")
-- Quest done, reward not gotten yet
elseif (quest(WERERAT_QUEST).status == QUEST_STATUS_COMPLETED) then
print_hook("#####yWererat Quest!\n")
print_hook("You have killed the chief wererat.\n")
print_hook("Perhaps you should see about a reward.\n")
print_hook("\n")
end
end,
["level"] = 10,
The bottom line shows the difficulty level of the quest.
Next are the hooks, which actually make something happen in the quest.
["hooks"] =
{
-- Start the game without the quest, need to request it
[HOOK_BIRTH_OBJECTS] = function()
quest(WERERAT_QUEST).status = QUEST_STATUS_UNTAKEN
end,
This means that when you start FuryBand, you do not have the quest. You have to request it from a building.
[HOOK_GEN_QUEST] = function()
if (player.inside_quest ~= WERERAT_QUEST) then
return FALSE
end
load_map(wererat_quest.MAP,2,2)
return TRUE
end,
This loads the quest map. Note that WERERAT_QUEST and wererat_quest.MAP,2,2 are two different things. WERERAT_QUEST is the actual quest and
wererat_quest is the map. Even though they have the same name, they are different. Lua recgonises them as being different, because of the upper and lower case letters.
If the map was in upper case, lua would think they were the same, and the game would crash when you tried to enter the quest map. this was a
mistake I made when doing my first quests.
[HOOK_STAIR] = function()
local ret
-- only ask this if player about to go up stairs of quest and hasn't won yet
if (player.inside_quest ~= WERERAT_QUEST) or (quest(WERERAT_QUEST).status == QUEST_STATUS_COMPLETED) then
return FALSE
end
if cave(player.py, player.px).feat ~= FEAT_LESS then return end
-- flush all pending input
flush()
-- confirm
ret = get_check("Really abandon the quest?")
-- if yes, then
if ret == TRUE then
-- fail the quest
quest(WERERAT_QUEST).status = QUEST_STATUS_FAILED
return FALSE
else
-- if no, they stay in the quest
return TRUE
end
end,
This controlls what happens if you go up the stairs of the quest level before you have killed Chief wererat. If you do this, then the quest
is automatically failed; the quest entrance disappears and you cannot take the quest again.
[HOOK_NEW_MONSTER] = function(r_idx)
-- Don't let Chief wererat be created until talking to Mayor
if (r_idx == test_monster_name("Chief wererat")) then
if (quest(WERERAT_QUEST).status == QUEST_STATUS_TAKEN) then
return FALSE
else
-- Haven't got the quest yet, don't generate him
return TRUE
end
end
return FALSE
end,
This creates the unique monster, the Chief wererat. As the comment line (second line down) explains, this hook makes the Chief wererat
appear in the quest level, once you have taken the quest. To create a unique quest monster, you have to add it into the r_info
(monster) file. Such a monster will have two particular flags. The flag SPECIAL_GENE makes sure that the Chief wererat only
appears where he is supposed to and the UNIQUE flag (of course) only allows one Chief wererat to be created.
[HOOK_MONSTER_DEATH] = function(m_idx)
m_ptr = monster(m_idx)
if (m_ptr.r_idx == test_monster_name("Chief wererat")) then
quest(WERERAT_QUEST).status = QUEST_STATUS_COMPLETED
msg_print(TERM_YELLOW, "You have killed the Chief wererat.")
end
end,
},
}
This controlls the death of Chief wererat and so sets the quest to completed. The brackets close the hook section, as there are no more
hooks after this.
The final part of the quest puts the actual quest into a building, so that you can request it.
-- Mayors office action
add_building_action
{
["index"] = 62,
["action"] = function()
-- the quest hasn't been requested already, right?
if quest(WERERAT_QUEST).status == QUEST_STATUS_UNTAKEN then
-- quest has been taken now
quest(WERERAT_QUEST).status = QUEST_STATUS_TAKEN
This part of the script puts the quest into a building of your choice. I'll explain how to do this at the end.
-- issue instructions
msg_print("A Chief of the wererats keeps invading our village!")
msg_print("He comes every full moon, and bites people, ")
msg_print("to turn them into wererats. The rest of the time, ")
msg_print("he lives in a cave, to the east of Kelrex.")
msg_print("Please enter this cave and kill him for me.")
return TRUE, FALSE, TRUE
These lines are the instructions you will recieve when you enter the building and request the quest.
-- if quest completed
elseif (quest(WERERAT_QUEST).status == QUEST_STATUS_COMPLETED) then
msg_print("Thank you! Let me give you a potion of experience.")
-- Give a potion of experience
local reward
reward = create_object(TV_POTION, SV_POTION_EXPERIENCE)
apply_magic(reward,1,FALSE,FALSE,FALSE)
reward.found = OBJ_FOUND_REWARD
inven_carry(reward, FALSE)
quest(WERERAT_QUEST).status = QUEST_STATUS_REWARDED
This part of the script controlls the reward. After you have completed the quest, when you return to the building and press "q",
the mayor will reward you. The lines mean:
local reward
This creates the reward.
reward = create_object(TV_POTION, SV_POTION_EXPERIENCE)
This line shows what type of reward is given.
apply_magic(reward,1,FALSE,FALSE,FALSE)
I think this means that there are no magic effects on the reward.
It is just a normal potion of experience.
reward.found = OBJ_FOUND_REWARD
inven_carry(reward, FALSE)
quest(WERERAT_QUEST).status = QUEST_STATUS_REWARDED
I'm not too sure about this part, however the last line sets the quest to rewarded, and so now the quest is finished.
-- if the player asks for a quest when they already have it, but haven't failed it, give them some extra instructions
elseif (quest(WERERAT_QUEST).status == QUEST_STATUS_TAKEN) then
msg_print("Please enter the cave and kill him for me.")
The comment line at the top explains what this section does.
-- quest failed or completed, then give no more quests
elseif (quest(WERERAT_QUEST).status == QUEST_STATUS_REWARDED) then
msg_print("I have no more quests for you.")
end
return TRUE
end,
}
The comment line here does the same. When the quest is either failed, or completed and you have been rewarded, then returning to the same building and requesting a quest again
will give the message on the third line i.e. "I have no more quests for you."
The bracket at the end is important and is used to close the script.
As this is a Gerband quest, it is difficult to transfer into a clean copy of FuryBand, because all the monsters are different. So, I will
just explain here how you would go about putting a quest into a clean copy of FuryBand
Save the quest as q_wererat.lua in the lib/scpt file. Of course, when you write your own quests you could call them anything, for example myquest.lua
However, they all need to have .lua on the end of them, as this tells the computer you want to save the file as a lua file.
In FuryBand, most quests are hard coded into the "ruling" buildings in a town (e.g the Mayor's Office, the Castle etc). If you just want to add in
a new quest along with the quests that are already there, open the st_ifo file in libs/edit. This file contains the information about
what different actions can happens in buildings. Find one of the ruling buildings. The Mayor's Office is number 10.
You will see the following info:
N:10:Mayor's Office
A:0:0:16:0:35:0
O:1:1:1:1
G:+:o
W:0
The line starting with A is the important one. The zero's are empty slots that can be filled with new building actions. The 16 and 35 are
building actions that are taken care of in the ba_info file. Open this file and find action 35.
N:35:Get a quest
C:0:0:0
I:6:0:q
This is the action that allows the player to take a hard coded quest. To cancel the hard coded quest, simply return to st_info file and delete
the 35 and replace with a zero.
To add in a new quest, type in the number 62 where the 35 was. This 62 corresponds to the 62 at the top area of the final part of the quest.
Remember :)
-- Mayors office action
add_building_action
{
["index"] = 62,
Now go back to ba_info and add in this info:
N:62:Get a quest
C:0:0:0
I:62:0:q
The q on the bottom line corresponds to the q you press in the game when you are getting the quest.
Or, if you have left in the hard coded quests, add in this:
N:62:Request a quest
C:0:0:0
I:62:0:r
The command "request a quest" and the r on the bottom line (for request) do not clash with the commands "Get a quest" and the letter q,
so you can take both the quest you have added in and the hard coded quests.
Then go into the misc file and find the lines:
# Maximum number of building actions in ba_info.txt
M:B:62
Replace the 62 with a 63, as you have added a new building action.
You also have to put the quest into a town. Open the file t_bree in lib/edit folder. You will see quest info, such as the info for the
wight quest:
# Default for Quest 18 = entrance is tree
F:x:96:3
############### Quest 9 - Wights Grave ###############
?:[EQU $QUEST9 1]
F:x:8:3:0:0:0:0:0:9
?:1
Copy this info in
F:w:89:3
?:[EQU $QUEST"Wererat quest" 1]
F:w:8:3:0:0:0:0:0:"Wererat quest"
?:1
On the lines of code, the 8 after the x and the w is very important. This number corresponds to the number 8 in the f_info file. Number 8 is a quest entrance.
The letter w indicates where the quest entrance will appear when you have taken the quest. It is the same as the x in the wight
quest. You can put the w anywhere you want on the town map. A w is used because this is a free letter, as the x y and z slots are
already used in Bree for quest entrances. If you put the wererat quest into a new town without any quests yet in it, you could use
x,y or z for the quest entrance symbol.
Lastly you need to add the quest to the init file, so that it is executed when you start up the game. Go into lib/scpt, open the init file and add in at the new
quest at the bottom of the following quests:
-- Add some quests
tome_dofile("bounty.lua")
tome_dofile("god.lua")
tome_dofile("fireprof.lua")
tome_dofile("library.lua")
tome_dofile("q_wererat.lua")