Author Topic: How to build quests using arrays  (Read 9023 times)

0 Members and 2 Guests are viewing this topic.

Offline erwin

  • Sr. Member
  • ****
  • Posts: 314
    • View Profile
How to build quests using arrays
« on: November 30, 2015, 07:51:55 pm »
To cut down on flag spam, here's how you can build quests using arrays, and a suggested method of doing so.

First - decide on the logic of the quest, as well as the variables needed for the quest. All this goes in one array.

Second - create a function (or three functions - a mob function, a room function, and a obj function) with the same script, which sets up the array. Here's an example (for a quest that wants you to kill the captain, find one helper's voucher, and to rescue a dog). Create the "basic" quest array which is when nothing is done for the quest.


Code: [Select]
if !%actor.varexists(my_quest)%
  set my_quest no_captain no_voucher no_rescue no_prog not_done
  remote my_quest %actor.id%
endif

The array (when someone vstats a player) will look like this:

Code: [Select]
my_quest: no_captain no_voucher no_rescue no_prog not_done

All such quest arrays should have two extra variables at the end, namely no_prog, and not_done, to signify the quest has not been started (no progress), and the quest has not been completed at least once (not_done). Obviously, you can re-name these variables to be anything you like, but it makes it easier for future people to debug your quest logic if you are not around on the MUD.

Now, suppose:

function 1 - corresponded to a ROOM function
function 2 - corresponded to a MOB function
function 3 - corresponded to an OBJ function

To start the quest, suppose it was a ROOM trigger that started the quest.
Code: [Select]
function 1
%echo% The room says: I will give you a quest.
set my_quest %actor.my_quest%
replace_word in_prog 4 my_quest

Equivalently:
Code: [Select]
function 1
%echo% The room says: I will give you a quest.
set my_quest %actor.my_quest%
if %my_quest.wordat(4)% != in_prog
  replace_word in_prog 4 my_quest
  remote my_quest %actor.id%
endif

To kill the captain, suppose it was a death script on the mob. This means that we are going to use the function corresponding to a mob function. Code would just be:

Code: [Select]
function 2
* If the actor did not have the variable, he has it now
set my_quest %actor.my_quest%
* replaces killed_captain with the first word in my_quest
replace_word killed_captain 1 my_quest
remote my_quest %actor.id%

See - no messy if statements needed. Or, to be a bit more efficient,
Code: [Select]
function 2
* If the actor did not have the variable, he has it now
set my_quest %actor.my_quest%
if %my_quest.wordat(1)% != killed_captain
  * replaces killed_captain with the first word in my_quest
  replace_word killed_captain 1 my_quest
  remote my_quest %actor.id%
endif

Or better still:
Code: [Select]
function 2
* If the actor did not have the variable, he has it now
set my_quest %actor.my_quest%
* Only sets the flag if actor started the quest - else killing the captain won't register, muahahahaha
if %my_quest.wordat(4)% == in_prog
  * replaces killed_captain with the first word in my_quest
  replace_word killed_captain 1 my_quest
  remote my_quest %actor.id%
endif

Note that the the snippets of code above for giving the quest / killing the captain are the same. One snippet doesn't do an if-check and updates the variables regardless of the value, but the other does an if-check. So for builders who doesn't understand if-checks, they can choose the first option for both and no bugs!

You do the same for other scripts. In fact, for any script which checks for that quest variable, calling the appropriate function (function 1 if it's a room script, function 2 if it is a mob script, and function 3 which is an obj script) and checking for the quest variables and replacing them works.

Then finally (suppose this script was on a mob):

Code: [Select]
function 2
set my_quest %actor.my_quest%
if %my_quest.wordat(1)% == killed_captain && %my_quest.wordat(2)% == got_voucher && %my_quest.wordat(3)% == rescued_dog
  * yay, let's check
  if %my_quest.wordat(5)% != done
    * first time doing this quest
    * %echo% great job, have this big reward
    replace_word done 5 my_quest
  else
    %echo% You have done this before, so here's this small reward
  endif
  * now, reset all questflags (in practice, good to have this as a function as well)
  replace_word 1 no_captain my_quest
  replace_word 2 no_voucher my_quest
  replace_word 3 no_rescue my_quest
  replace_word 4 no_prog my_quest
  remote my_quest %actor.id%
endif

Equivalently, instead of replacing all 4 words, you can just do
Code: [Select]
set my_quest no_captain no_voucher no_rescue no_prog done
remote my_quest %actor.id%
but - be careful if you store other variables on this flag (eg, maybe whether a person has a zoneflag or not. Then it gets messy - so replace_word is the better option)

What is the in_prog / no_prog variable for? Well, the quest journal shows active quests. So to check if a quest is in progress, the quest journal just checks for a in_prog / no_prog flag. in_prog flag means the player is doing this quest, else the player isn't. Basically, when the player starts off the quest, but hasn't completed anything yet.
« Last Edit: November 30, 2015, 07:55:34 pm by erwin »

Offline erwin

  • Sr. Member
  • ****
  • Posts: 314
    • View Profile
Re: How to build quests using arrays
« Reply #1 on: November 30, 2015, 08:58:12 pm »
One reason why we use functions (which can take up 3 vnums if you use the three different types of triggers - room, obj, mob), even though there's three lines in:

Code: [Select]
if !%actor.varexists(my_quest)%
  set my_quest no_captain no_voucher no_rescue no_prog not_done
  remote my_quest %actor.id%
endif

which could be easily copied and pasted.

Suppose you made a typo in the quest array, or decide that you wanted (more) multi-steps in the quests which means creating new variables.

Then - changing all the scripts you've made would be hard - what if you've had 20 or so scripts? Whereas, for example, if all you did was added new variables, eg instead of

Code: [Select]
myquest: no_captain no_voucher no_rescue no_prog not_done
you wanted more variables like

Code: [Select]
no_captain no_voucher no_rescue no_prog not_done var1 var2 var3
Then, you could edit these three functions to be:
Code: [Select]
if !%actor.varexists(my_quest)%
  set my_quest no_captain no_voucher no_rescue no_prog not_done var1 var2 var3
  remote my_quest %actor.id%
elseif %actor.varexists(my_quest)%
  * Basically, check to see if variables 6, 7, and 8 exist. If they don't exist,
  * then wordat 6 and wordat 7 would be "null"
  * Equivalently, could check the strlen.
  set my_quest %actor.my_quest%
  if %my_quest.wordat(6)% == %my_quest.wordat(7)%
    *means old questflag, so let's rebuild
    insert_word var1 6 my_quest
    insert_word var2 7 my_quest
    insert_word var3 8 my_quest
    remote my_quest %actor.id%
  endif
endif

and it's fixed.
« Last Edit: November 30, 2015, 09:04:40 pm by erwin »