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")