More quests help
By Cosmic Gerbil
In the first quests help I showed how to write a basic quest. However there are lots of different types of quests. First let’s look at a house quest, where your reward will be a house to store you belongings in.
The house quest
It is easy to write a lua quest script for a house, as all you need to do is remove the object reward, so that you can give a house as a reward. Look at this example from my wererat quest below.
-- 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 can be condensed down into a simple message line, where the mayor, king or whoever the ruler of the town is, tells you that you have earned a house as you reward. Look at this example from my Wumpus quest:
-- if quest completed
elseif (quest(WUMPUS_QUEST).status == QUEST_STATUS_COMPLETED) then
msg_print("Well done! Please accept the hut as your home.")
quest(WUMPUS_QUEST).status = QUEST_STATUS_REWARDED
All that happens here is that the town ruler tells you that you can have a house and the quest is set to rewarded. The actual gaining of a house is controlled through the quest information in a town file.
If you are adding a house quest and you don’t want to disable the custom quests, then you will need to use a town that does not have any house quests, or create your own new town.
Let’s say that you have created a new town. You have added a mayor’s office to your town map and you have also added the building action to “get a quest”. On your town map you will need to add something similar to the following:
# Default for Wumpus quest = entrance is quest entrance
F:y:8:3:0:0:0:0:0:"Wumpus quest"
?:[EQU $QUEST"Wumpus quest" 2]
F:y:74:3:0:0:0:0:0:7
?:[EQU $QUEST"Wumpus quest" 5]
F:y:74:3:0:0:0:0:0:7
?:1
The “y” after the “F” can be any letter that is not used as a town feature. You will want to add this y to your map. Put it somewhere relevant to your quest…for example on the front of a building to represent a door. After all, this y is indicating where your house will be.
In the two lower lines of code you will see there is a number 74 after the “y”. This 74 in the f_info file corresponds to a building. The number 7 after all the zeros is the code for a house, as can be seen in the file st_info.
The numbers 2 and 5 are quest information numbers which are hardcoded. Look at the info from the source code below:
#define QUEST_STATUS_IGNORED -1
#define QUEST_STATUS_UNTAKEN 0
#define QUEST_STATUS_TAKEN 1
#define QUEST_STATUS_COMPLETED 2
#define QUEST_STATUS_REWARDED 3
#define QUEST_STATUS_FAILED 4
#define QUEST_STATUS_FINISHED 5
#define QUEST_STATUS_FAILED_DONE 6
You don’t have too worry too much about this, it is just showing which numbers correspond to quest status. So by putting in the numbers 2 and 5, this means that when you have completed the quest and you leave the quest level, it will turn into a number 8, which is your new house.
Killing a set number of monsters
In this example, rather than kill one unique monster, to complete the quest you have to kill all the monsters on the quest level. This number is determined by however many you want to add onto the quest level map. So if you draw 10 monsters onto a quest amp, then you have to kill all ten before you have completed the quest. The number of monsters should correlate to the difficulty level of the quest. If you have designed a quest for a player with experience level 10-15 then you will not want to add 10 Great Wyrms of Power ;)
You will probably not need to use HOOK_NEW_MONSTER because this is only for quest uniques and there won’t be any quest uniques in a “kill a set number of monsters” quest.
So using HOOK_MONSTER_DEATH, you will need to put the following code:
[HOOK_MONSTER_DEATH] = function()
-- if they're in the quest and haven't won, continue
if (player.inside_quest ~= STOREROOM_QUEST) or (quest(STOREROOM_QUEST).status == QUEST_STATUS_COMPLETED) then
return FALSE
end
i = 1
count = -1
while i <= m_max do
local monster = m_list[i]
if (monster.r_idx > 0) and (monster.status <= MSTATUS_ENEMY) then
count = count + 1
end
i = i + 1
end
if count == 0 then
quest(STOREROOM_QUEST).status = QUEST_STATUS_COMPLETED
msg_print(TERM_YELLOW, "You have killed all the monsters.")
end
end,
},
}
The name STOREROOM_QUEST is just an example of my quest. Your quest will have whatever name you want to call it in place of STOREROOM_QUEST.
I am not really sure of all the syntax, except that it is counting until all the monsters are dead. MSTSTUS_ENEMY (of course) means that the monsters are hostile to you.
Rescuing a friendly monster
In this type of quest, you will first need to create a friendly monster in the r_info file, so that you can rescue them. Add this monster onto your quest map, preferably guarded by lots of hostile monsters ;)
The way I do a rescue script is by adding in HOOK_CHAT, so that when you talk to the friendly monster he (or she) will tell you how to rescue them. Once you have chatted, the quest is set to completed. Here is an example from my soldier quest:
[HOOK_CHAT] = function(m_idx)
-- which monster are we chatting to?
m_ptr = monster(m_idx)
-- Is it the Young soldier?
if (m_ptr.r_idx == 407) then
cmsg_print(TERM_L_BLUE, "The young soldier says to you,")
cmsg_print(TERM_YELLOW, "'Alright, there. You came to rescue me? Great!")
cmsg_print(TERM_YELLOW, "One of the elite ologs has got the key")
cmsg_print(TERM_YELLOW, "that will unlock my chains.'")
cmsg_print(TERM_L_BLUE, "You search the bodies of the elite ologs,")
cmsg_print(TERM_L_BLUE, "find the key and free the soldier.")
cmsg_print(TERM_L_BLUE, "He is wounded, but he is able to walk.")
cmsg_print(TERM_L_BLUE, "The young soldier thanks you, and limps out of the cave.")
-- The soldier is free!
delete_monster_idx(m_idx)
quest(SOLDIER_QUEST).status = QUEST_STATUS_COMPLETED
msg_print(TERM_YELLOW, "You have rescued the solider.")
return TRUE
end
end,
},
}
The line “if (m_ptr.r_idx == 407) then” looks up the number of the soldier in the r_info file. So the script is basically saying…if the monster you are attempting to chat to is the soldier, then continue onto the next part of the script.
The next part consists of a number of messages that appear on the screen as the soldier chats to you. TERM_YELLOW and TERM_BLUE show what colour the messages will be printed in. The soldier’s conversation appears in yellow, to separate it from the messages that are just narrating.
As you can see by the messages, just chatting to the soldier frees him, but to make him actually leave the quest level, you need the line of code “delete_monster_idx(m_idx)”. This removes the soldier.
Then the quest is set to complete and a message is printed to tell you that you have rescued the soldier.
The “return TRUE” prevents the message “the monster does not want to chat” appearing after you have chatted to the soldier.
These are just some examples of quests than can be coded in lua. There are many other types of quests and the best way to learn about them is to look at as many quest scripts as possible. Nearly all of my quest examples have come from my module GerBand and I learned to code by looking at quests, mostly written by Falcon (the maintainer of the Annals of Ea mod). Most mod writers and maintainers are only too happy to help you with any questions you may have about writing your quests. Quests are great fun and in many cases, the only limits for a quest is your imagination J