Show Posts

This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.


Messages - erwin

Pages: 1 [2] 3 4 ... 21
16
General Discussions / Re: 4D
« on: December 22, 2015, 03:25:08 pm »
probably

17
Suggestions & Ideas / Re: Questjournal v1
« on: December 21, 2015, 12:58:21 pm »
Update 21st December (or when Molly puts it in)

Two questcards added to the journal, card 34 and card 37. Logic works better with these two quests. If you don't have the quest journal, it might be a good time to get it, as you can't skip steps anymore. 

There are 15 more questcards to go before the entire Prehistoric dimension is done.

18
Suggestions & Ideas / Re: Questjournal v1
« on: December 18, 2015, 11:37:38 am »
Update 18th December (or when Molly puts it in)

Two questcards added to the journal, card 36 and card 38. Logic works better with these two quests.

19
Suggestions & Ideas / Re: Questjournal v1
« on: December 12, 2015, 10:38:32 pm »
Update 12th December (or when Molly puts it in)

Two questcards added to the journal, card 56 and card 57. Some bugs fixed with the Hades quest. If you can't do it before, you can now. If you have an imm char, the annoying message in a lot of rooms
Code: [Select]
[ Room 76xx :: wdamage: bad syntax ]
has been fixed.

20
Suggestions & Ideas / Re: Questjournal v1
« on: December 09, 2015, 12:55:07 am »
Update: 9th December (or when Molly puts it in)

One questcard added to the questjournal, Questcard 55, plus some fun stuff.

Polyphemos works better now.

21
Suggestions & Ideas / Re: Questjournal v1
« on: December 08, 2015, 04:09:10 pm »
Added a feature in the Quest Journal, but only for (certain) imms:

If you think a quest in the Quest Journal is bugged, you can also ask an imm to type: questcheck card<num> debug <your player character's name>.

Example: questcheck card3 debug stile

They will then see the respective quest flags that you have, plus (some) commentary about the status / where you are at during the quest. 

22
Scripting Board / Suggested Practices for Scripting
« on: December 05, 2015, 11:42:32 pm »
In the past, we had lots of good practices for scripting.

For example, here's some best practices in the past:

1. The difference between set and eval and when to use them
2. The difference between %actor% versus %actor.name%, and when to use them.
3. mjunk an object (instantly) when the player gives it to a mob so he can't steal it back
4. Setting a flag when the player completes the quest, so the rewards are lesser the second / subsequent time the player completes it

However, I think we can have more "suggested practices for scripting", as well as use the 4D only DG functions which Mordecai / Rynald / others have coded in for future best practices.

These "suggested practices" will be focused on debugging and understanding scripts, in the context of the builder fixing his/her own scripts a few weeks / months after he/she worked on it, as well as someone else fixing the script if the previous builder has retired.

I will first describe what imms face when debugging. For example, Molly made a post here

http://4dimensions.org/forum/index.php/topic,682.msg5045.html#msg5045

which shows the questflags a player has. Here's some potential issues (maybe):

a) The list is quite long
b) Some variable names are not intuitive. For example, the variable zeddletter versus jack versus asking. For the first variable, this depends on how much the person knows about the MUD. He might make a guess that it's from the Midlands zone, and is given to the player when he interacts with Zedd about a letter (giving a letter / receiving a letter / asking him to write a letter?). Having knowledge about the quests in the MUD would give the player the knowledge of what the variable does, so that's fine.

How about the other two variables? Well, a solution would be to tsearch all jack, or tsearch all asking to find the respective scripts, and then try to figure out what they do. That takes effort, but can we do better?

Thus: Suggested Practices for Scripting shall be about intuitive variable names, and intuitive variable values. I'll try to present this in a way of "Problem", and then the "Solution", as suggested best practices.

Problem: Variable names are not intuitive. For example, asking is less intuitive than zeddletter

Solution: Create intuitive variable names

Problem: There are five helper vouchers. Suppose upon picking each one up, we store the value as 1, and we have the five variables

Code: [Select]
helper_voucher_1: 1
helper_voucher_2: 1
helper_voucher_3: 1
helper_voucher_4: 1
helper_voucher_5: 1

on the player. Once goes through the Newbie School, he is convinced he picked up all 5 vouchers, but lost them in a DT. Poor Once. He tells Kvetch: You can vstat me, and search for these 5 variables. Unfortunately, vstat doesn't show the variables in alphabetical order, nor the time the variable was created. Must Kvetch painstakingly look at all his variables and find the 5 of them in 15 pages? What if Once didn't get the 4th voucher, but Kvetch thinks he has, so she tries to find it? Or worse, what if Once did get the 4th voucher, but because of the spam, Kvetch didn't see it?

Solution 1: Ask a coder to fix vstat to show whether the player has the variable or not.
Solution 2: Use an array and store the values in one variable, called helper_voucher

Code: [Select]
helper_voucher: found found not_found found not_foundor
Code: [Select]
helper_voucher: 1 0 1 1 0

Problem: Once goes to the Midlands, and does a quest. He thinks it's bugged, and he wants to know the variables relating to the zone, so he can fix it himself in the BP. Unfortunately, apart from zeddletter, he doesn't know what other variables on his pfile relate to the Midlands. He wonders if he should search through the "quest actions" done, or the names of the mobs in the Midlands just in case the builder used that as a variable?

Solution: Have the variable name include the zone as well. Even if the builder / imm online doesn't know anything about the quest, he or she can say: "This is the variable / these are the variables related to the zone, I'll send this on to another imm."

In other words, incorporate the zone name in the variable.

Examples of two variables:
Code: [Select]
crete_zone: not_killed_minotaur killed_theseus bullgirl_not_done
and
Code: [Select]
crete_zone: killed_minotaur killed_theseus bullgirl_done

Most imms / players who see that flag would immediately know what the player has done (or not done) in the zone. 

Thus, a suggested practice for scripting is to minimize the number of variables by using arrays, and replace_word.

23
Suggestions & Ideas / Re: Questjournal v1
« on: December 04, 2015, 12:56:45 am »
Update: 4th December (or when Molly puts it in)

One questcard added to the questjournal, Questcard 3, plus some fun stuff.

Some key changes:

1) Trading mobs in Greek Archipelago (Cyclades) will now remember what you have given them over reboots and copyovers (yay).

2) The quest Cyrene's Lover did not have any flags at all, so with the quest journal, a flag has been added on to the bracelet in order to show the quest has been completed. This means you should go re-do the quest to get the flag, as removing the bracelet and re-wearing it will not work. However, the item will not poof.


3) There's two major typos / bugs fixed in (some) trading mobs in the Cyclades. Some of you might have spotted the first, the second is what Virisin and Erwin used to exploit. Maybe.

24
Scripting Board / Re: Trading Scripts
« on: December 03, 2015, 03:04:35 am »
Also, note that
Code: [Select]
set speecha1 thank you for the diamonds
set speechb1 blah blah love you

etc is optional. If the mob does a generic thank you, you might as well have
Code: [Select]
if %traded_item% != 0
  *Great, no rejected items. Now to find which one
  wait 1
  mjunk %object%
  say thank you!
  wait 1
  * Reward goes here, corresponding to the traded_item
  set tp_reward %tp_reward_vec.wordat(%traded_item%)%
  nop %actor.trade(%tp_reward%)%
  eval d %actor.level%*%exp_reward_vec.wordat(%traded_item%)%
  nop %actor.exp(%d%)%
 
  * Halt this script if we have successfully traded! 
  halt
endif

Similarly, if you have fewer / more speeches to give, you could have
Code: [Select]
set speecha1 thank you for the diamonds
set speechb1 blah blah love you
set speechc1 laa
if you wanted the mob to say three lines, for example.

You might then say: "What if for one item, I want the mob to do something different?" Well, then you can have
Code: [Select]
if %traded_item% != 0
  *Great, no rejected items. Now to find which one
  wait 1
  mjunk %object%
  say thank you!
  wait 1
  * Reward goes here, corresponding to the traded_item
  set tp_reward %tp_reward_vec.wordat(%traded_item%)%
  nop %actor.trade(%tp_reward%)%
  eval d %actor.level%*%exp_reward_vec.wordat(%traded_item%)%
  nop %actor.exp(%d%)%
 
  if %traded_item% == 4
    %echo% This is a special item! Do special stuff here
  endif
 
  * Halt this script if we have successfully traded! 
  halt
endif

25
Scripting Board / Re: Trading Scripts
« on: December 03, 2015, 02:58:09 am »
One last post to explain what the first loop does. If it's complicated, pretend it's a DG function like .contains or replace_word (which are simple - but the code behind them is not so simple).

Think of this loop as a DG function which takes into input all your item#. In the first post, we had item1, item2, ... , item6.

Then, it outputs the number where the vnum occurs, and 0 otherwise.

So, think of this as a "black box". If we had

Code: [Select]
set item1 111 112 113 114 120

set item2 200 201 214 206

set item3 300 301 333 399

set item4 400 420 490

set item5 500

set item6 666 669

Then if the object vnum was 400, our loop would return the number 4 (because 400 was in item4).

If the object vnum was 206, our loop would return the number 2 (because 206 was in item2).

If the object vnum was 10000, our loop would return the number 0 (because 10000 is not in any of the items).

Returning the number is useful, because we can use this number to check for rewards.

We had (for example)
Code: [Select]
set speecha1 thank you for the diamonds
set speechb1 blah blah love you
set tp_reward_vec 1 4 3 2 4 9
set exp_reward_vec 1000 3000 1000 1000 4000 4000

Think of the number that the loop returns (or black box) helpful, by identifying what speech to give. Eg, if we had the number 4, then we will give speecha4 and speechb4, the value of the TP in the 4th number (2), and the value of the XP in the 4th number (1000).

So this portion
Code: [Select]
if %traded_item% != 0
  *Great, no rejected items. Now to find which one
  wait 1
  mjunk %object%
  * evals the correct speech corresponding to the traded item
  eval tosaypartone %%speecha%traded_item%%%
  eval tosayparttwo %%speechb%traded_item%%%

  say %tosaypartone%
  say %tosayparttwo%
  wait 1
  * Reward goes here, corresponding to the traded_item
  set tp_reward %tp_reward_vec.wordat(%traded_item%)%
  nop %actor.trade(%tp_reward%)%
  eval d %actor.level%*%exp_reward_vec.wordat(%traded_item%)%
  nop %actor.exp(%d%)%
 
  * Halt this script if we have successfully traded! 
  halt
endif

if you read it line by line, it just checks for the number (in %traded_item%), finds the correct speech to give with
Code: [Select]
  eval tosaypartone %%speecha%traded_item%%%
  eval tosayparttwo %%speechb%traded_item%%%

and gives the right TP reward and XP reward in
Code: [Select]
  set tp_reward %tp_reward_vec.wordat(%traded_item%)%
  nop %actor.trade(%tp_reward%)%
  eval d %actor.level%*%exp_reward_vec.wordat(%traded_item%)%
  nop %actor.exp(%d%)%

If you have problems reading this code, here's another way to look at this.

We have the reward code as
Code: [Select]
if %traded_item% != 0
  *Great, no rejected items. Now to find which one
  wait 1
  mjunk %object%
  * evals the correct speech corresponding to the traded item
  eval tosaypartone %%speecha%traded_item%%%
  eval tosayparttwo %%speechb%traded_item%%%

  say %tosaypartone%
  say %tosayparttwo%
  wait 1
  * Reward goes here, corresponding to the traded_item
  set tp_reward %tp_reward_vec.wordat(%traded_item%)%
  nop %actor.trade(%tp_reward%)%
  eval d %actor.level%*%exp_reward_vec.wordat(%traded_item%)%
  nop %actor.exp(%d%)%
 
  * Halt this script if we have successfully traded! 
  halt
endif

There's lots of variables, so let's pretend that we gave object vnum 206. Running the "black box" loop, we have %traded_item% to be 2, because we had
Code: [Select]
set item2 200 201 214 206
and 206 is inside.

Let's replace %traded_item% in the code by 2 (the value of %traded_item%)
Code: [Select]
if 2 != 0
  *Great, no rejected items. Now to find which one
  wait 1
  mjunk %object%
  * evals the correct speech corresponding to the traded item
  eval tosaypartone %%speecha2%%
  eval tosayparttwo %%speechb2%%

  say %tosaypartone%
  say %tosayparttwo%
  wait 1
  * Reward goes here, corresponding to the traded_item
  set tp_reward %tp_reward_vec.wordat(2)%
  nop %actor.trade(%tp_reward%)%
  eval d %actor.level%*%exp_reward_vec.wordat(2)%
  nop %actor.exp(%d%)%
 
  * Halt this script if we have successfully traded! 
  halt
endif

Okay, we still have more variables now like %%speecha2%%, %%speechb2%%, and %tp_reward_vec.wordat(2)%, etc, but it should be easier to see. If not, we'll go through the code one more time and replace these variables by what we've defined.

Code: [Select]
if 2 != 0
  *Great, no rejected items. Now to find which one
  wait 1
  mjunk %object%
  * evals the correct speech corresponding to the traded item
  eval tosaypartone thank you for the rubies
  eval tosayparttwo blah blah love you

  say %tosaypartone%
  say %tosayparttwo%
  wait 1
  * Reward goes here, corresponding to the traded_item
  * because 4 was the word at 2 for the tp_reward_vec
  set tp_reward 4
  nop %actor.trade(%tp_reward%)%
  * because 3000 was the word at 2 for the exp_reward_vec
  eval d %actor.level%*3000
  nop %actor.exp(%d%)%
 
  * Halt this script if we have successfully traded! 
  halt
endif

And that's it :)








------------

This is the loop to find the value of %traded_item% for further info. If it bothers you, pretend it's a DG function that gives you what you want :)

Code: [Select]
while %i% <= %max_items%
  * look at first itemvec
  * Need to eval this!
  eval item_to_check %%item%i%%%
 
  set j 1
 
  while %j% != 0
    extract curr_item %j% %item_to_check%
    if !%curr_item%
      * Loop will stop
      set j 0
    else
      if %curr_item% == %currvnum%
        set traded_item %i%
        break
      endif
      eval j %j% + 1
    endif
  done
  if %traded_item% != 0
    eval i %max_items% + 1
  endif
  eval i %i% + 1
done

I'm reluctant to say: "Use this code even if you don't understand it", because that's where lots of errors can occur. But in that case, we also shouldn't use cool DG commands like
Code: [Select]
.contains
.wordat()
.strlen
replace_word
etc, because we don't know how to "script" the equivalent in DG. But we still use them anyway. For example, replace_word as a DG function is easy to understand
Code: [Select]
set sentence This is a dog and it is barking
replace_word boy 4 %sentence%
will give This is a boy and it is barking. But not many of us will be able to script the equivalent in DG.
Code: [Select]
set sentence This is a dog and it is barking
* want to replace boy in word 4
set word_to_replace boy
set num_at_replace 4

* After setting sentence, word_to_replace, num_at_replace, then:

* Find the maximum length of this sentence
set len 0
while %len%
  eval len %len% + 1
  extract tmp %len% %sentence%
  if !%tmp%
    break
  endif
done
eval len %len% - 1

*len is now the length of the sentence (ie number of words)


extract newsentence 1 %sentence%
set i 2
while %i% < %num_at_replace%
  extract nextword %i% %sentence%
  set newsentence %newsentence% %nextword%
  eval i %i% + 1
done

set newsentence %newsentence% %word_to_replace%
eval i %i% + 1
while %i% <= %len%
  extract nextword %i% %sentence%
  set newsentence %newsentence% %nextword%
  eval i %i% + 1
done

However, hold on! This code won't work if we wanted to replace the first word. So the actual code would be something like
Code: [Select]
set sentence This is a dog and it is barking
* want to replace that in word 1
set word_to_replace that
set num_at_replace 1

* After setting sentence, word_to_replace, num_at_replace, then:

* Find the maximum length of this sentence
set len 0
while %len%
  eval len %len% + 1
  extract tmp %len% %sentence%
  if !%tmp%
    break
  endif
done
eval len %len% - 1

*len is now the length of the sentence (ie number of words)

if %num_at_replace% == 1
  set newsentence %word_to_replace%
  set i 2
else
  extract newsentence 1 %sentence%
  set i 2
  while %i% < %num_at_replace%
    extract nextword %i% %sentence%
    set newsentence %newsentence% %nextword%
  eval i %i% + 1
  done
  set newsentence %newsentence% %word_to_replace%
  eval i %i% + 1
endif

while %i% <= %len%
  extract nextword %i% %sentence%
  set newsentence %newsentence% %nextword%
  eval i %i% + 1
done

It's a pain to read, especially without comments. Most builders probably don't understand what that does, and wouldn't dare to use it, but they would use
Code: [Select]
replace_word

even though it does the same thing.

So maybe you have a question: If we can put that terrible looking loop in the form of
Code: [Select]
replace_word

then why can't we do the same for
Code: [Select]
while %i% <= %max_items%
  * look at first itemvec
  * Need to eval this!
  eval item_to_check %%item%i%%%
 
  set j 1
 
  while %j% != 0
    extract curr_item %j% %item_to_check%
    if !%curr_item%
      * Loop will stop
      set j 0
    else
      if %curr_item% == %currvnum%
        set traded_item %i%
        break
      endif
      eval j %j% + 1
    endif
  done
  if %traded_item% != 0
    eval i %max_items% + 1
  endif
  eval i %i% + 1
done
?

Well, let's see how replace_word works. The syntax is
Code: [Select]
replace_word blah 4 sentence
And you can read this as: "We are going to replace the 4th word of sentence by the word blah"

But, what the "black box" loop does is: Given any number of possible traded items (could be 6, could 10, could 2), the loop will return the item number. How would such an input look like if we had such a function? Eg if we wanted to check if vnum 12345 was in item1 to item3, then..
Code: [Select]
blackboxloop 12345 item1 item2 item3
?

But if we had 10 items, then
Code: [Select]
blackboxloop 12345 item1 item2 item3 item4 item5 item6 item7 item8 item9 item10
?

What if the builder transposed two numbers, eg
Code: [Select]
blackboxloop 12345 item2 item1 item3
, would it still return the right number? So maybe it's better if the loop was in DG instead of a function...

26
Scripting Board / Re: Trading Scripts
« on: December 01, 2015, 03:28:56 am »
Also, you can create two functions for the traded_item loop, and the rejected_item loop.

Then, for all traders in the MUD, just call these two functions whenever you trade something, so in the future, code might just look like

Code: [Select]
set item1 111 112 113 114 120
set speecha1 thank you for the diamonds
set speechb1 blah blah love you

set item2 200 201 214 206
set speecha2 thank you for the rubies
set speechb2 blah blah love you

set item3 300 301 333 399
set speecha3 thank you for the garnets
set speechb3 blah blah love you

set item4 400 420 490
set speecha4 thank you for the sapphires
set speechb4 blah blah love you

set item5 500
set speecha5 thank you for the quartz
set speechb5 blah blah love you

set item6 666 669
set speecha6 thank you for the topaz
set speechb6 blah blah love you

set max_items 6

set tp_reward_vec 1 4 3 2 4 9
set exp_reward_vec 1000 3000 1000 1000 4000 4000

set rej_item1 1000 1005 1110
set rej_speech1 I don't want fool's gold.

set rej_item2 2000 2222
set rej_speech2 I don't want iron pyrite.

set rej_max_items 2

set currvnum %object.vnum%

* Loop for traded_item function
function 122

if %traded_item% != 0
  *Great, no rejected items. Now to find which one
  wait 1
  mjunk %object%
  * evals the correct speech corresponding to the traded item
  eval tosaypartone %%speecha%traded_item%%%
  eval tosayparttwo %%speechb%traded_item%%%

  say %tosaypartone%
  say %tosayparttwo%
  wait 1
  * Reward goes here, corresponding to the traded_item
  set tp_reward %tp_reward_vec.wordat(%traded_item%)%
  nop %actor.trade(%tp_reward%)%
  eval d %actor.level%*%exp_reward_vec.wordat(%traded_item%)%
  nop %actor.exp(%d%)%
 
  * Halt this script if we have successfully traded! 
  halt
endif

* If traded_item is zero,  then it's a rejected item
return 0
wait 1
emote makes a refusing gesture.

* Loop for rejected items
function 125

if %rej_traded_item% != 0 
  eval tosay %%rej_speech%rej_traded_item%%%
  say %tosay%
  halt
else
  say This is a generic statement saying I don't like your item.
endif


27
Scripting Board / Trading Scripts
« on: December 01, 2015, 03:09:43 am »
Here's one way to do a trading script, which would account for lots of different items being traded, and rejected.

However, before the script is posted, let's think about how the script would work. In the old days, the script would be something like (in pseudo code):
Code: [Select]
if %item.vnum% == 100 || %item.vnum% == 101
  %send% %actor% Thank you for trading item number 100 or item 101!
  %send% %actor% Let me give you a reward
elseif %item.vnum% == 103
  %send% %actor% Thank you for trading item number 103!
  %send% %actor% Let me give you a reward
elseif %item.vnum% == 105
  %send% %actor% Thank you for trading item number 105!
  %send% %actor% Let me give you a reward!
elseif %item.vnum% == 110 || %item.vnum% == 114 || %item.vnum% == 120
  %send% %actor% Sorry, I don't take this type of items
  %send% %actor% Go away!
endif

However, we see that a lot of things are repeated. For example, for a correct item, the code is always of the form:
Code: [Select]
if %item.vnum% == "correct vnum"
  mob gives some reward for the item
  mob says thank you
endif

For example, if the mob can receive 10 different items, then for each item, we might have the (duplicated) code
Code: [Select]
wait 1s
mjunk %object%
say thank you
give TP reward
give XP reward
10 items would mean 50 lines of duplicated code. This takes up a lot of space. Can we do better? (yes).

So here's a way to do it

Code: [Select]
* First, set the number of items that the mob trades. Suppose the mob is a jeweller, and trades the six items only
* The numbers correspond to the correct vnums
* Speech is optional - could have lots of variables for this, just make sure the last "character" of the speech is the
* same number of the item. But if you want the mob to say the same thing for each item
* then in the loop below, could just "say thank you".

* Item 1 corresponds to diamonds
set item1 111 112 113 114 120
set speecha1 thank you for the diamonds
set speechb1 blah blah love you

* Item 2 corresponds to rubies
set item2 200 201 214 206
set speecha2 thank you for the rubies
set speechb2 blah blah love you


* Item 3 corresponds to garnets
set item3 300 301 333 399
set speecha3 thank you for the garnets
set speechb3 blah blah love you

* Item 4 corresponds to sapphires
set item4 400 420 490
set speecha4 thank you for the sapphires
set speechb4 blah blah love you


* Item 5 corresponds to quartz
set item5 500
set speecha5 thank you for the quartz
set speechb5 blah blah love you


* Item 6 corresponds to topaz
set item6 666 669
set speecha6 thank you for the topaz
set speechb6 blah blah love you


* Set the maximum items the mob trades, in this case 6
set max_items 6

* Set the rewards for each of them. So item 1 (diamonds) would get 1 TP and 1000 xp, and item 6 (topaz)
* would get 9 TP and 4000 xp

set tp_reward_vec 1 4 3 2 4 9
set exp_reward_vec 1000 3000 1000 1000 4000 4000

* You can set rejected items as well, for example, suppose the mob rejects fool's gold and iron pyrite

* rej_item 1 corresponds to fool's gold
set rej_item1 1000 1005 1110
set rej_speech1 I don't want fool's gold.

* rej_item 2 corresponds to iron pyrite
set rej_item2 2000 2222
set rej_speech2 I don't want iron pyrite.

* Set maximum number of rej items. In this case, 2
set rej_max_items 2

* Now comes the main part

set currvnum %object.vnum%

* We want to find which item it is, so we loop through all items (in item1 to item 6)
set traded_item 0
set i 1

while %i% <= %max_items%
  * look at first itemvec
  * Need to eval this!
  eval item_to_check %%item%i%%%
 
  set j 1
 
  while %j% != 0
    extract curr_item %j% %item_to_check%
    if !%curr_item%
      * Loop will stop
      set j 0
    else
      if %curr_item% == %currvnum%
        set traded_item %i%
        break
      endif
      eval j %j% + 1
    endif
  done
  if %traded_item% != 0
    eval i %max_items% + 1
  endif
  eval i %i% + 1
done

* This will create a variable traded_item which will tell you what item it is
* For example, if traded_item is 4, then the player gave sapphires
* If the traded_item was 0, means it was none of the 6 possible items

if %traded_item% != 0
  *Great, no rejected items. Now to find which one
  wait 1
  mjunk %object%
  * evals the correct speech corresponding to the traded item
  eval tosaypartone %%speecha%traded_item%%%
  eval tosayparttwo %%speechb%traded_item%%%

  say %tosaypartone%
  say %tosayparttwo%
  wait 1
  * Reward goes here, corresponding to the traded_item
  set tp_reward %tp_reward_vec.wordat(%traded_item%)%
  nop %actor.trade(%tp_reward%)%
  eval d %actor.level%*%exp_reward_vec.wordat(%traded_item%)%
  nop %actor.exp(%d%)%
 
  * Halt this script if we have successfully traded! 
  halt
endif

* If traded_item is zero,  then it's a rejected item
return 0
wait 1
emote makes a refusing gesture.

* Check if it was either fool's gold or iron pyrite, same loop as above
set rej_traded_item 0
set i 1

while %i% <= %rej_max_items%
  * look at first itemvec
  * Need to eval this!
  eval item_to_check %%rej_item%i%%%
  set j 1 
  while %j% != 0
    extract curr_item %j% %item_to_check%
    if !%curr_item%
      * Loop will stop
      set j 0
    else
      if %curr_item% == %currvnum%
        set rej_traded_item %i%
        break
      endif
      eval j %j% + 1
    endif
  done
  if %rej_traded_item% != 0
    eval i %rej_max_items% + 1
  endif
  eval i %i% + 1
done

* same thing

if %rej_traded_item% != 0 
  eval tosay %%rej_speech%rej_traded_item%%%
  say %tosay%
  halt
else
  say This is a generic statement saying I don't like your item.
endif

What are the benefits of this script?

1. You can trade LOTS of different items. So if we traded 50 items, writing the code the long way might mean duplicating 50*5 = 250 lines of code.

With this script, we save on 250 lines of code (there is extra code because of the loop, but that has a constant number of lines whether you trade 5 items or 500 items).

2. You don't need lots of ifchecks like
Code: [Select]
if %object.vnum% == 1000 || %object.vnum% == 1001 || %object.vnum == 1002
for every item. For example, did you notice in the code above, I left out a % in %object.vnum == 1002? Hard to spot such errors! However, with the above script, all a builder needs to do is to edit the first few lines. Fewer chances of mistakes.

Yay!

28
Scripting Board / Re: How to build quests using arrays
« 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.

29
Scripting Board / 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.

30
Suggestions & Ideas / Re: Questjournal v1
« on: November 30, 2015, 10:44:13 am »
Update: 30th November

Two questcards added to the questjournal, Questcard 6 and Questcard 20, plus some fun stuff.

Quest in Questcard 6 had a small "bug" - if you had remorted in between doing the quest, it *may* have become unsolvable. But it's solvable now (don't forget to reset flags, or at least use the questjournal once to update your old flags).

Quest in Questcard 20 was a repeatable quest for the same reward, and thus no flags were checked for this. So even if you've done this quest before, it will still be counted as "undone". A flag has been added to check for completion.

Pages: 1 [2] 3 4 ... 21