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 ... 21
1
Scripting Board / Re: Function triggers / Function of the Day
« on: February 20, 2017, 03:30:58 pm »
and of course...someone told me that widee should be wide e (oops) ; plus the line

Code: [Select]
nop %actor.exp(-50000)%
probably should be

Code: [Select]
nop %actor.exp(-%am%)%
isn't it lucky we use triggers?

2
Scripting Board / Re: Function triggers / Function of the Day
« on: February 20, 2017, 03:24:38 pm »
A function to avoid repetition

Here's an example. In Fenizia, the mobs are extremely interactive, and some of them accept gifts. If you kill a particular mob in Fenizia, and present its head to all the other NPCs, they will kill you.

So for example, we would have many different RECEIVE triggers that look like this:

Code: [Select]
if %obj.vnum% == 12345
  * do stuff
elseif %obj.vnum% == 12346
  * do more stuff
elseif %obj.vnum% = 22222
  wait 1
  drop %object%
  wait 1 s
  widee %actor%
  emote exclaims, 'Oh, my God! Are you mad?
  %echo% Do you have ANY idea what consequences this might lead to?'
  wait 1 s
  emote says, 'This is a total disaster, you moron!
  %echo% He was not a man that wouldn't make some precautions either...
  eval am (%actor.level%*(2000+%random.200%)))
  nop %actor.exp(-50000)%
  mkill %actor%
endif

These twelve lines are repeated across several different triggers. Which is great, unless there are errors in these lines. So a much cleaner way is to do this.

Code: [Select]
Name: 'Kill Mob Function',  VNum: [ 7958], RNum: [ 2982]
Trigger Intended Assignment: Mobiles
Trigger Type: FUNCTION , Numeric Arg: 100, Arg list: None
Commands:
wait 1
drop %object%
wait 1 s
widee %actor.name%
emote exclaims, 'Oh, my God! Are you mad?
%echo% Do you have ANY idea what consequences this might lead to?'
wait 1 s
emote says, 'This is a total disaster, you moron!
%echo% He was not a man that wouldn't make some precautions either...
eval am (%actor.level%*(2000+%random.200%))
nop %actor.exp(-50000)%
mkill %actor%

And then we can replace the above code with

Code: [Select]
if %obj.vnum% == 12345
  * do stuff
elseif %obj.vnum% == 12346
  * do more stuff
elseif %obj.vnum% = 22222
  function 7958
endif

Now, you might say that it's only twelve lines - we don't need a function trigger. However, did you notice we made two changes? We changed widee %actor% to widee %actor.name%, and we fixed a bracketing issue in when evaluating the experience points to deduct from the player.

Imagine making this change across all NPCs :( -> eg, you can type tsearch all 7958 to see the impact of this.

It becomes a bigly issue when there are longer chunks of scripts repeated throughout, which means lots and lots of changes. This not just becomes terribly annoying to fix, but introduces more chance of errors (what if we delete an extra line? or delete an else? or we didn’t catch a word wrap, and cut off part of what a mob is supposed to say / do?)

So - if you're a builder, and you find yourself repeating large chunks of code, consider using functions! Errors can be fixed in the function trigger itself, rather than fixing the same error across many scripts.

This is especially important if the script you are repeating does things which may have a great impact on players, such as giving them experience points, setting questflags, etc.

So if you repeat a chunk of code lots of times - use functions!

3
Scripting Board / Function triggers / Function of the Day
« on: February 20, 2017, 03:22:49 pm »
Function triggers are something special in 4 Dimensions MUD, as they provide many opportunities to builders and scripters alike. Unlike specific type triggers, eg MOB triggers, OBJ triggers, and ROOM triggers, function triggers can be attached to MOBs, ROOMs, and OBJs without any errors.

The most common usage of function triggers is to "extend" a script. For example, we have a mob that tells a super long story (trigger modified):

Code: [Select]
Name: 'story',  VNum: [ 2350], RNum: [  706]
Trigger Intended Assignment: Mobiles
Trigger Type: Speech , Numeric Arg: 1, Arg list: story stories
Commands:
if %actor.is_pc%
  wait 5s
  %send% %actor% The mob tells a story...
  * ...
  * ...
  * some time later
  function 2356
endif

What does function 2356 do? Well, it continues the story

Code: [Select]
Name: 'Story Conclusion',  VNum: [ 2356], RNum: [  712]
Trigger Intended Assignment: Mobiles
Trigger Type: FUNCTION , Numeric Arg: 0, Arg list:
Commands:

emote takes a deep breath.  Will he finally finish?
wait 10s
%send% %actor% blah blah blah
*...
*...

So you could replace "function 2356" in trigger 2350 by the entire DG script in 2356.

For those with some background in programming, you might say: "That's not all functions can do", and you'd be right. So we'll give examples on how to use functions in this forum post

4
Scripting Board / Re: Questjournal OLC
« on: February 12, 2017, 04:13:38 pm »
Questcheck debug mode: What is it, why should it be used?

Every zone these days have quests, and we don't know the intention of the builder when he/she designed the quests. Barring common typos or checking the wrong variables, or not putting a 1s delay before a trigger, we do not know if the quest is functioning as intended.

For example, Questcard11 (Bereaved Woman) could not be solved, because a key involved had a maxload of 1. One of the quests in Questcard17 was also unsolvable because an item did not reload at all.

Playtesting new zones may also not work in getting these bugs out - because we don't know if the player has not done correct steps, or because there really is a "logic bug".

Yesterday, there was a player saying that Marion was "bugged" because she didn't respond - but the actual fact was that the player did not say a certain keyword.

If an imm did not know the quest structure, then fixing any "bugs" might make the quest intentionally "easy", or impossible to complete.

Debug mode in the questjournal tries to resolve the above issues, and also reduces the possibilities of logical errors.

Here is an example of a how it might work.

Suppose we had a questline where the player must do the following.
1. Wait till midnight
2. Eavesdrop on a conversation at Recall behind Buck and Jubei.
3. Say the phrase "hash table". If "splay tree" is said, then the quest cannot be completed (flags are reset, player must start again).
4. Say "yes" to Buck
5. Buck will ask player to get a key, giving the key to Buck will complete the quest.

A good function behind debug mode (when the imm types qcheck <num> debug <playername>) would work like this:

Code: [Select]
* debugger is the imm, player is the player
* pretend the quest variable is questcard200_buck

* Get the player's flag
set questcard200_buck %player.questcard200_buck%

if %questcard200_buck% == 0
  %send% %debugger% %player.name% is not on this quest!
  %send% %debugger% {cx
  %send% %debugger% %player.name% must wait till midnight to start this quest.
elseif %questcard200_buck% == 1
  %send% %debugger% %player.name% has waited till midnight.
  %send% %debugger% {cx
  %send% %debugger% %player.name% must type {cceavesdrop{cx to listen to Buck and Jubei.
elseif %questcard200_buck% == 2
  %send% %debugger% %player.name% has eavesdropped on Buck and Jubei.
  %send% %debugger% {cx
  %send% %debugger% %player.name% must say {cchash table{cx to proceed.
  %send% %debugger% If %player.name% says {ccsplay tree{cx, the flags will be reset and %player.heshe% must start again.
elseif %questcard200_buck% == 3
  %send% %debugger% %player.name% has said {cchash table{cx to Buck.
  %send% %debugger% {cx
  %send% %debugger% %player.name% must say {ccyes{cx to move on.
elseif %questcard200_buck% == 4
  %send% %debugger% %player.name% has been asked by Buck to get a key.
  %send% %debugger% {cx
  %send% %debugger% %player.name% must go to A Dark Mine (14024), and kill the bat there to get the key. Returning the key to Buck completes the quest.
endif

What does this really do? If a player is "stuck" on the quest, then the imm just needs to type qcheck 200 debug <player>. This function will look at the player's flag, and tell the imm where the player is on the quest, and what the player needs to do.

For example, if the flag was at 2, then the imm would see:
Code: [Select]
Lionheart has eavesdropped on Pix and Jubei.

Lionheart must say hash table to proceed.
If Lionheart says splay tree, the flags will be reset and he must start again.

Then, the imm can:
a) Ask Lionheart if he is really at the stage after eavesdropping on Pix and Jubei
b) Asked what Lionheart has tried saying

So: If Lionheart said: "I did everything except say hash table", the imm can say: "This is not a bug, you did not say hash table to proceed."

Similarly, if Lionheart said: "I said hash table, but nothing happened", then the imm can say: "Yup, you are bugged. Let me fix this."

Or perhaps Lionheart might say: "Buck asked me to get a key, but I can't find it." The imm can then type qcheck 200 debug Lionheart, and would get the message:

Code: [Select]
Lionheart has been asked by Buck to get a key.

Lionheart must go to A Dark Mine (14024), and kill the bat there to get the key. Returning the key to Buck completes the quest.

The imm can then check to see if the key exists on the bat, what the maxload is, etc. (This is more common than you think when zones get updated, but quests that may rely on objects / etc on these zones don't update.)

On the builder side when creating such functions, this forces the builder to actually think through the entire quest tree. For example, when I write such functions, I do so step by step, and make sure the debug mode is saying the right thing. This helps to prevent a lot of logic errors, as I ensure every step works for a character on 6001. If you don't do this, you die.

On the other hand, there are some bad ways to write a debug function (please don't do this). Here's another way to write it:

Code: [Select]
* debugger is the imm, player is the player
* pretend the quest variable is questcard200_buck

* Get the player's flag
set questcard200_buck %player.questcard200_buck%

if %questcard200_buck% == 0
  %send% %debugger% %player.name% is not on this quest!
  %send% %debugger% {cx
  %send% %debugger% %player.name% must eavesdrop on Buck at midnight.
elseif %questcard200_buck% == 2
  %send% %debugger% %player.name% must say {cchash table.
elseif %questcard200_buck% == 4
  %send% %debugger% %player.name% must get the key for Buck to complete this quest.
endif

While this function also works, what information does it give the imm? The player actually had to: "Wait till midnight", and then "type eavesdrop" to proceed in the quest. But the phrase: "Lionheart must eavesdrop on Buck at midnight" doesn't convey any meaningful information.

Furthermore, "Lionheart must get the key for Buck to complete this quest." is not very helpful. Note: You can get away with this if it's a unique object, eg, Buck wants a pink colored ski jacket, since an imm can type "where ski jacket", but "where key" isn't helpful :(

If it helps, the way to think about the debug mode is:

For an imm that does not know anything about the zone or nothing about scripting - or both, debug mode should allow the imm to tell the player: "You are right, the quest is bugged!", or "You haven't done something yet."


Fixing the error - send a note to the original builder of the zone, or any of the scripters / coders online.


5
Scripting Board / Re: Trigger of the .... something
« on: February 09, 2017, 10:44:08 pm »
New Trigger Of The Day: Generic Craps Game.

You roll two dice. If you roll a 2, 3, or a 12, you lose. If you roll a 7 or a 11, you win. Otherwise, you keep on rolling the dice.

If you roll a 7, you lose. If you match the first number you rolled, you win.

Who knows? In some alternate universe, this might be a first homework question for an intro programming course. Or not.

Code: [Select]
* Trigger 59010

* waybread, potpie, grapes, lollipop, cookies and cream
set prize_vec 10 3009 6346 34080 1758
%send% %actor% You hand over some gold coins to the dealer.
nop %actor.gold(-10000)%
wait 2 s
%send% %actor% The dealer says, 'Right, let's play a game.'
wait 2 s
%send% %actor% The dealer takes out a pair of dice, and he throws them up in the air!
wait 2 s
%send% %actor% The dice come tumbling down...
wait 2 s
set d1 %random.6%
set d2 %random.6%
%send% %actor% The first die showed a {cR%d1%{cx!
wait 2 s
%send% %actor% The second die showed a {cR%d2%{cx!
eval first_roll %d1% + %d2%
wait 2 s
%send% %actor% The total sum is {cY%first_roll%{cx!
wait 2 s
if %first_roll% == 7 || %first_roll% == 11
  %send% %actor% You win! YAY!
  wait 2 s
  %send% %actor% The dealer gives you a prize!
  set pnum %random.5%
  %load% obj %prize_vec.wordat(%pnum%)% %actor%
  wait 2 s
  %send% %actor% Do you want to play again?
elseif %first_roll% == 2 || %first_roll% == 3 || %first_roll% == 12
  %send% %actor% You lose! Bummer!
  wait 2 s
  %send% %actor% The dealer smirks and says: Better luck next time, pal!
else
  * roll until you get back your previous roll
  set this_roll 0
  %send% %actor% The dealer says, 'Since the sum of the dice was not a 2,3,7,11, or 12, I'll roll them again!'
  wait 2 s
  %send% %actor% He continues, 'You will win if I roll back a %first_roll%, and lose if I roll a 7.
  wait 2 s
  %send% %actor% Otherwise, I'll keep on rolling the dice.'
  set counter 1
  while %counter% < 2
  *while (%this_roll% != 7 && %this_roll% != %first_roll%)
    wait 2 s
    %send% %actor% The dealer takes out a pair of dice, and he throws them up in the air!
    wait 2 s
    %send% %actor% The dice come tumbling down...
    wait 2 s
    set d1 %random.6%
    set d2 %random.6%
    %send% %actor% The first die showed a {cR%d1%{cx!
    wait 2 s
    %send% %actor% The second die showed a {cR%d2%{cx!
    eval this_roll %d1% + %d2%
    wait 2 s
    %send% %actor% The total sum is {cY%this_roll%{cx!
    wait 2s
    * some weird stuff about multi arguments for while
    if %this_roll% == %first_roll%
      set counter 10
    elseif %this_roll% == 7
      set counter 10
    else
      %send% %actor% The dealer says, 'Gotta roll again!'
    endif
  done
  if %this_roll% == 7
    %send% %actor% You lose! Bummer!
    wait 2 s
    %send% %actor% The dealer smirks and says: Better luck next time, pal!   
  else
    %send% %actor% You managed to get back the value of the first roll!
    wait 2 s
    %send% %actor% You win! YAY!
    wait 2 s
    %send% %actor% The dealer gives you a prize!
    set pnum %random.5%
    %load% obj %prize_vec.wordat(%pnum%)% %actor%
    wait 2 s
    %send% %actor% Do you want to play again?   
  endif
endif


* This mob will be 1s of Recall ; at least until a copyover is done.

6
Scripting Board / Questjournal OLC
« on: February 09, 2017, 08:18:03 pm »
Here's a brief description of what to do (in practice) with the Questjournal OLC. If there are any questions, feel free to shoot Roland, Maco, or me a note.

Current Questcards will be in this post ; New questcards will be in a separate post.

The goal of the questjournal is to:
  • Prevent spam / too many single questflags when you vstat a player
  • Provide some information to any imms when trying to debug a quest. Eg, is it a logic problem in a quest, or typo problem in a quest, or some other issue?
  • Allow players to track their quest status
  • Allow players to track their trading status
  • Allow players to reset questflags

The following post gives design suggestions on how to transfer current questcards to the quest journal.

Essentially, you need to know the zone in and out.

First, you need to know what ALL the flags are, and how they should be replaced with a new flag. The new flag should be something of the form "questcard<num>_<questname>" etc so it's easier to track flags.

Second, you need to plan how many questflags you want to have, and how the variables in the questflag will change based on the player's status in the quest, and which variables to reset. You should do this before editing any triggers, etc.

Third, while planning the questflags, you should also think of constructing a debug mode trigger at the same time. This helps to prevent errors.

Fourth, you want a mechanism that replaces the flags properly. Remember that questflags are checked when a player interacts with any mob, room, or object that is part of the quest. Function triggers are useful here if you don't want to duplicate code, and prevent errors.

Fifth - extensive testing on 6001 to make sure that every step of the quest / every possible combination works (or doesn't work). Ensure that the debug trigger tells the imm who is debugging where the player is, and what the player needs to do. More information is always better.

A warning for old-> new flag replacing.

Old->New flag replacing

If a player types qcheck <num> <something>, the quest journal will check if the player has the questflags given in the OLC. If not, the quest journal will auto set them. This is great for new quests, but bad for old quests.

Why is this? For example, we will look at Quest 6 (Thetis' Lover). The old quest has 11 questflags, thetis1, thetis2, ... thetis11.

Previously, at a stage, the code would look something like this:

Code: [Select]
if %actor.varexists(thetis2)%
  %echo% You are on stage 2!
else
  %echo% Good day, %actor.name%
endif

Of course, what we really need to do is to create a variable (maybe thetis) - with and set it to different values for each stage, eg
Code: [Select]
set thetis 2
remote thetis %actor.id%
because then we could just do:
Code: [Select]
if %actor.varexists(thetis)%
  if %actor.thetis% == 2
    %echo% You are on stage 2
  endif
endif

So far - this makes sense, right?

In the previous version where we had a quest journal, we put the replacing of flags in a function. Here is how this would have looked like.
Code: [Select]
* Assume this is trigger 12345 as a function

if !%actor.varexists(questcard6_thetis)%
  set questcard6_thetis 0
  if %actor.varexists(thetis1)%
    set questcard6_thetis 1
    rdelete thetis1 %actor.id%
  endif
  if %actor.varexists(thetis2)%
    set questcard6_thetis 2
    rdelete thetis2 %actor.id%
  endif
  if %actor.varexists(thetis3)%
    set questcard6_thetis 3
    rdelete thetis3 %actor.id%
  endif

  * etc etc

  remote questcard6_thetis %actor.id%
endif
etc all the way till thetis11. Note that while the code looks long, and would have eleven if checks (for thetis1 to thetis 11), the first if branch is only ever executed once.

So what would usually be done is this:

Code: [Select]
* Create Thetis flag, and replace questflag
function 12345
if %actor.questcard6_thetis% == 2
  %echo% You are on stage 2
endif

Notice that in the above code, we did not check if the variable questcard6_thetis existed, because our function 12345 would have created this for us automatically if it did not exist. Furthermore, the time taken for the MUD to execute this is almost the same time if we had just replaced function 12345 by
Code: [Select]
if %actor.varexists(questcard6_thetis)%

However: We have a problem when the quest journal auto puts the flags on players. The replacing does not take place. So players may have duplicate flags - both old flags and new flags.

Ahhah, you say. But that is an easy fix - we can move the inner if statements out, eg:
Code: [Select]
* new function

if !%actor.varexists(questcard6_thetis)%
  set questcard6_thetis 0
  remote questcard6_thetis %actor.id%
endif

if %actor.varexists(thetis1)%
  set questcard6_thetis 1
  remote questcard6_thetis %actor.id% 
  rdelete thetis1 %actor.id%
endif

if %actor.varexists(thetis2)%
  set questcard6_thetis 2
  remote questcard6_thetis %actor.id% 
  rdelete thetis2 %actor.id%
endif

if %actor.varexists(thetis3)%
  set questcard6_thetis 3
  remote questcard6_thetis %actor.id% 
  rdelete thetis3 %actor.id%
endif

* etc etc

But in this case - each time the actor is on any part of the quest chain and this function is called, eleven if statements will be checked, potentially adding lag to the MUD.

What's the solution?

We have come up with a nifty way to add all questflags on the player once they: go into Limbo, walk into Recall, die, or use the Time Guardian. Of course, you have to send a note to Pix (or Roland) what the new questflags are, and how the old questflags should be replaced. It's even better if you give us a trigger that does this in the first place.

PS: If you are a player when a current quest is added to the quest journal, we suggest you walk to Recall / use the Time Guardian. Otherwise, you might get the new set of flags which signal that you haven't done a quest, but without the replacing of old flags done. While this might allow you to get more token rewards, it also means if you remove and wear a quest equip, it might purge itself.

7
Scripting Board / Re: Trading Scripts
« on: January 31, 2017, 10:25:48 am »
Trading Script V6.

This is in room 47300 in 6001. More info can be found there, but feel free to play about with it. Send a note for more requests for different types of triggers.

The intention is to make trading scripts "easier to write" - eg, there's actually no code below, it's just setting what you want the player to do / mob to react. It's also easier to debug (since no code), and also somewhat easier to see if the rewards are appropriate.

Code: [Select]
Name: 'Trigger for Generic Trade',  VNum: [47300], RNum: [ 8677]
Trigger Intended Assignment: Mobiles
Trigger Type: Receive , Numeric Arg: 100, Arg list: None
Commands:


if !%actor.is_pc%
  halt
endif

**** Set parameters
**** What the player sees if supposed to get multiple tradepoints
**** eg, tp_start_plural NUM TRADEPOINTS tp_end_plural

set tp_start_plural Bob hands over
set tp_end_plural tradepoints.
set tp_sing Bob hands over 1 tradepoint.

**** What the player sees if supposed to get multiple brass tokens
**** eg, brass_start_plural NUM TOKENS brass_end_plural

set brass_start_plural Bob hands over
set brass_end_plural brass tokens.
set brass_sing Bob hands over 1 brass token.

**** What the player sees if supposed to get multiple brass tokens
**** eg, bronze_start_plural NUM TOKENS bronze_end_plural

set bronze_start_plural Bob hands over
set bronze_end_plural bronze tokens.
set bronze_sing Bob hands over 1 bronze token.


* Accepted Trade items only
* ALWAYS PLACE PARTIAL ITEMS AT THE END
* Partial items - eg trade FIVE grapes for ONE wine
* You get up to FIVE messages for everything
* Unlimited items to trade (in theory)

* Total number of items traded

set max_items 5

* Non partial items, eg trade ONE item to get reward

set ordinary_items 3

* Partial items, eg trade FIVE grapes for reward

set part_items 2

* Makes it easier for questjournal in future to keep track of trades
* Trade_flag can be: trade_egypt_mobname etc etc

set trade_flag trade_thiszone_thismob

* Place the part_items at the end.
* Up to 5 messages, with delay in seconds
* If you need more messages, send a note

* Delay between mob messages
set delay 2

* Flags for items
* If none, don't need to have the flag there
* item_x : list vnums, or short desc
* type_x : Is it a vnum, or short desc?
* msg_Ax, msg_Bx, msg_Cx, msg_Dx, msg_Ex : What mob says
* reward_x : reward is in terms of XP TP BRASS BRONZE
* item_reward_x : vnum of item reward
* how_many_x : For partial items, how many to get a reward?
* reward_after_x : The reward after getting all partial items
* item_reward_after_x : The item reward after getting all partial items

* Put the three ordinary items first

* Item 1 (diamonds)

* Bob will accept one item, will have three actions
* This item gets 1000 XP * level, 10 TP, 0 brass, 0 bronze

set item_1 8465
set type_1 vnum
set msg_A1 Bob says, 'Thank you for trading this item'
set msg_B1 Bob smiles.
set msg_C1 Bob says, 'I have nothing else to say.'

* Reward is in terms of XP, TP, BRASS, BRONZE
set reward_1 1000 10 0 0


* Item 2 (logs)
* Bob will accept any item with short desc logs, will have four actions
* This item gets 5000 XP * level, 4 TP, 1 brass, 2 bronze
* This item will also get a object reward vnum 2523

set item_2 log
set type_2 short
set msg_A2 Bob says, 'Thank you for trading the log item'
set msg_B2 Bob smiles.
set msg_C2 Bob says, 'I have given you a small gift of a chair.'
set msg_D2 Bob thanks you heartily.
* Reward is in terms of XP, TP, BRASS, BRONZE
set reward_2 5000 4 1 2
set item_reward_2 2523

* Item 3 (bread)
* Bob will accept two items: 10, 9291 and has one action
* This item gets 1 TP


set item_3 10 9291
set type_3 vnum
set msg_A3 Bob says, 'I like bread.'
set reward_3 0 1 0 0

* Reminder, always put partial items last

* Item 4 (grapes)
* Bob will accept two items: 6346, 44495 and has two actions
* This item gets no reward each time
* Bob has a message to count them, eg msg_counter_before NUMGIVEN msg_counter_after

* But when all 5 are given (how_many_4 flag)
* Player gets 1000 xp * level, 5 TP, 1 brass, 0 bronze
* Player also gets object 6349

* Item 4 (grapes - trade 5 to get 1 wine)
set item_4 6346 44495
set type_4 vnum
set how_many_4 5
set msg_A4 Bob says, 'Thanks for the grapes'
set msg_B4 Bob says, 'I will store them to make wine'

set msg_counter_before_4 Bob says, 'Keep them coming,
set msg_counter_after_4 out of 5!

set reward_4 0 0 0 0
set msg_after_A4 Bob says, 'You have given me 5 grapes'
set msg_after_B4 Bob says, 'I will give you some wine'

set reward_after_4 1000 5 1 0
set item_reward_after_4 6349


* Item 5 (ear)
* Bob will accept one items: 15083
* This item gets 1 TP each time it is given
* Bob has a message to count them, eg msg_counter_before NUMGIVEN msg_counter_after

* But when all 10 are given (how_many_5 flag)
* Player gets 1000 xp * level, 10 TP, 10 brass, 10 bronze

set item_5 15083
set type_5 vnum
set how_many_5 10
set msg_A5 Bob says, 'Thanks for the wolf ear'
set msg_B5 Bob says, 'I will give you 1 TP.
set reward_5 0 1 0 0

set msg_counter_before_5 Bob says, 'Look at these ears piling up,
set msg_counter_after_5 out of 10!

set reward_after_5 1000 10 10 10
set msg_after_A5 Bob says, 'I'm going to give you a bonus!'
set msg_after_B5 Bob says, 'Tokens and stuff and more'
set msg_after_C5 Bob says, 'But no items.'


******** Rejected items
* same thing

set rej_max_items 2

* Bob rejects items 8704, 1404 with a special message

set rej_item_1 8704 1404
set rej_type_1 vnum

set rej_msg_A1 Bob says, 'I don't like gold.'
set rej_msg_B1 Bob says, 'Go away.'

* Bob rejects items with short desc flower with a special message


set rej_item_2 flower
set rej_type_2 short

set rej_msg_A2 Bob says, 'I hate flowers.'
set rej_msg_B2 Bob says, 'Go away.'

***** Default message

set rej_default_A Bob says, 'What is this crap?'
set rej_default_B Bob says, 'Go away.'

**** The magic function
function 47391

8
Scripting Board / Re: Trading Scripts
« on: January 29, 2017, 11:18:51 pm »
Trading Script v4, until Roland puts in a Trading OLC to render this obsolete. It's still pretty funky.

tstat 2140 for this

All you need is to set the flags (below), basically:

1) max_items  (number of items this mob accepts)
2) na_flag (nas depending max items. If the mob accepts 5 items, then na_flag should be: set na_flag na na na na na)
3) flagname (flag to set on actor call it whatever you want, here I've called it trade_crete_tino
4) item and "speech" (rather more of what actor sees) flags, up to 3 different ones. Eg.

Code: [Select]
* items are what the mob accepts. For example, there's lots of wolf ears in the MUD, so you could do something like
set item1 1234 2222 5828

* if 1234, 2222, 5828 were vnums of wolf ears

* You get up to three consecutive actions the player sees in order.
* You don't need to use all three of them. So for example, you can have

set item1 52614
set speecha1 Tino says, 'Good! I can always use more hemp twine!'

* where item1 only has one line the player sees

set item2 12345
set speecha2 Gollum says, 'A ring, great!"
set speechb2 Gollum says, 'My precioussssssss'
set speechc2 Gollum caresses the ring eagerly.

* item 2 has three lines the player sees

set item3 123 6839 5824 52850
set speecha3 The player sees this.
set speechb3 and this.

* item 3 has three lines the player sees

* items / speeches are always set in consecutive order

5) tp_reward_vec: TP in order of the items given
6) exp_reward_vec: Exp in order of the items given
7) brass_reward_vec: Brass tokens in order of the items given

8) rej_max_items: How many "special" items will the mob reject? Same like above, we have variables rej1, rej2, ..., and rejspeecha1, rejspeechb1, rejspeechc1, .... etc.

9) genreja, genrejb, genrejc - 3 potential variables for generic rejection speech, again in consecutive order

Here's an example.

Code: [Select]
if !%actor.is_pc%
  halt
endif
* Molly, Kvetch, whoever wants to add MORE trading stuff here, follow the
* comments
*
* If you want to reduce the number of items traded, it's a bit more complex.
* Send me a note, or any of the coders.
* Ideally, we should probably write this in C instead of DG scripts.
*
* Flag: trade_crete_tino
* Currently, can trade THREE items, and mob rejects ONE item
*
*********
*********
*** The Edit Place
*************************
* Edit the below if you want to change the number of items traded
set max_items 3
*************************
* na_flag is basically THREE nas (or however max_items nas)
set na_flag na na na
*************************
* flagname - the flag for the trading
set flagname trade_crete_tino

* hemp twine
set item1 52614
set speecha1 Tino says, 'Good! I can always use more hemp twine!'

* sail cloth
set item2 52797
set speecha2 Tino says, 'Great! We were about to run out of sailcloth completely.'

* thin rope
set item3 52615
set speecha3 Tino says, 'Great, that thinrope will be enough for me to finish this batch of sails.'

* TP and EXP for each item
set tp_reward_vec 1 8 4
* This has no brass token
* set brass_reward_vec 0 0 0 1 0 0
set exp_reward_vec 1000 4000 3000

* rejected items

set rej_max_items 1

* sails
set rej1 44673 44674
set rejspeecha1 Tino says, 'Dude, I SELL sails, I don't buy them.
set rejspeechb1 So you can keep any sails you've made for yourself.' 


****
* Generic rejection speech
set genreja Tino says, 'I don't trade in such things.'
*
*

* This function will fix length of questflag, eg if more items were
* added
* Required inputs: na_flag, flagname, and max_items

function 2151

* BEFORE CALLING 21627
* Make sure you have defined a prefix for the items traded
* Above it's item1, item2, item3, item4, item5, item6, so prefix is item
* Also make sure you've set max_items, which ought to be ABOVE

* For the purists: function 21627 takes in an input prefix, and max_items,
* gives an output item_in_list

set prefix item
function 21627
set traded_item %item_in_list%

if %traded_item% != 0
  ** The reward for great trading items
  function 2152
  halt
endif


* Let's do the same for rejected items for special messages
* We are calling the same function 21627 again, but with
* rejected items as input
* So max_items is now rej_max_items, and prefix is rej

set max_items %rej_max_items%
set prefix rej
function 21627
set rej_traded_item %item_in_list%

function 2153

**** END

9
Suggestions & Ideas / Questjournal with cards 1-22
« on: January 07, 2017, 05:38:50 am »
Hi all,

Molly says if we get 50% of questcards done, we can post this on Mudconnect as this is something unique. Currently, Questcards 1-22 are done. So, instead of sitting at Recall, you can go 2 s to Bart, and say 'journal', or 'javert and chauvelin' to get a quest journal.

You can then (for the first 1-22 quests), check the following, and post here if there's any problems, etc.

1. Do all commands with questcard work?
   - questcheck card<num>
   - questcheck card<num> status
   - questcheck card<num> reset
   - questcheck card<num> {things particular to this card?}

2. Does the Quest Tree work?
   - can you complete the quest? (major)
   - does quest journal update correctly? - check this for every step you take.
   - are flags transferred over from previous quest (some old quests could be repeated multiple times for same reward, so there was never a flag)
   - does doing "wrong" things screw up the quest (this is probably more for builder of original quest rather than questjournal - we have reset. but good to debug)

3. Formatting (low priority)
   - Is the formatting okay? Typos?

4. Debug mode / Documentation (imm only)
   - Is it clear?
   - Is it easy to fix errors?

10
Scripting Board / Re: Trading Scripts
« on: January 06, 2017, 07:22:55 am »
Trading script v3, uses real examples. Imms only need to edit the first part, and can add as many or as few items as wanted. Script takes care of the rest.

Wishlist: Can we hardcode this somehow? Something like medit / oedit / sedit. tradeedit?

Note: We're setting a flag for this, so we can also track how many items players have traded, for that vendor.
Code: [Select]

if !%actor.is_pc%
  halt
endif
* Molly, Kvetch, whoever wants to add MORE trading stuff here, follow the
* comments
*
* If you want to reduce the number of items traded, it's a bit more complex.
* Send me a note, or any of the coders.
* Ideally, we should probably write this in C instead of DG scripts.
*
* Flag: trade_winter
* Currently, can trade SIX items, and mob rejects FOUR items
*
*********
*********
*** The Edit Place
* Edit the below if you want to change the number of items traded
set max_items 6
* na_flag is basically SIX nas (or however max_items nas)
set na_flag na na na na na na

* thinrope
set item1 52615
set speecha1 Bob says, 'OK, most of the mountaneers buy a thin rope from me.'

* thickrope
set item2 52616
set speecha2 Bob says, 'Right, thinropes are in most demand of course.
set speechb2 But there are situations when the mountaineers need thicker ropes too.
set speechc2 I've seen ten of them dangle over a precipice from the same rope.'

* longrope
set item3 52617
set speecha3 Bob says, 'Thanks! Just what I wanted.
set speechb3 I need a longrope in reserve in case some of them mountaneers falls into a deep ravine.'

* ropeladder
set item4 52618
set speecha4 Bob says, 'Great! I was just out of ropeladders!'

* wool yarn
set item5 52619 52620
set speecha5 Bob says, 'Yup, wool yarn is popular around here.'

* wool cloth
set item6 52624 52625
set speecha6 Bob says, 'Mmm, wool cloth is just what we need.
set speechb6 None of the other kinds keep you warm enough.'

* TP and EXP for each item
set tp_reward_vec 5 13 28 0 3 7
set brass_reward_vec 0 0 0 1 0 0
set exp_reward_vec 3000 6000 8000 10000 1000 1000


* rejected items

set rej_max_items 3

* cotton thread
set rej1 3266 30957
set rejspeecha1 Bob says, 'No thanks, I'm really just interested in wool yarn and cloth.
set rejspeechb1 It's too cold for other materials here.'

* silk
set rej2 52651 52661
set rejspeecha2 Bob says wistfully, 'I wish I could afford it.
set rejspeechb2 But nobody around here would buy silk, And it's too cold for it anyway.'

* cloth
set rej3 3267 30958 44653
set rejspeecha3 Bob says, 'Sorry, I cannot afford silk, and neither can my customers.'


****
* Can stop here for most cases. Below is generic stuff.
*
set trade_winter %na_flag%
if !%actor.varexists(trade_winter)%
  remote trade_winter %actor.id%
elseif %actor.varexists(trade_winter)%
  * Check to see if we have any changes in the length if this var exists
  set findnumwords %actor.trade_winter%
  function 21625
  * Check to see if variable max_items (set above) is equal
  * to number of words in trade_winter, eg if you modified above
  * flag to trade NINE items instead of SIX.
  if %max_items% != %num%
    * someone added more items so that's fine
    set ub 0
    while %ub% < %num%
      eval ub %ub% + 1
      *%echo% Replacing...
      replace_word %findnumwords.wordat(%ub%)% %ub% trade_winter
    done
    remote trade_winter %actor.id%
  endif
endif

set trade_winter %actor.trade_winter%

* BEFORE CALLING 21627
* Make sure you have defined a prefix for the items traded
* Above it's item1, item2, item3, item4, item5, item6, so prefix is item
* Also make sure you've set max_items, which ought to be ABOVE

* For the purists: function 21627 takes in an input prefix, and max_items,
* gives an output item_in_list

set prefix item
function 21627
set traded_item %item_in_list%

** The reward for great trading items
** Can mostly ignore this part
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%%%
  eval tosaypartthree %%speechc%traded_item%%%

  %send% %actor% %tosaypartone%
  if %tosayparttwo%
    %send% %actor% %tosayparttwo%
  endif
  if %tosaypartthree%
    %send% %actor% %tosaypartthree%
  endif
  wait 1
  * Reward goes here, corresponding to the traded_item
  set tp_reward %tp_reward_vec.wordat(%traded_item%)%
  nop %actor.trade(%tp_reward%)%
  if %tp_reward% == 1
    emote hands over %tp_reward% tradepoint.
  elseif %tp_reward% > 1
    emote hands over %tp_reward% tradepoints.
  endif
  eval d %actor.level% * %exp_reward_vec.wordat(%traded_item%)%
  nop %actor.exp(%d%)%
  * tokens
  set bronze_reward %brass_reward_vec.wordat(%traded_item%)%
  set counter 0
  while %counter% < %bronze_reward
    eval counter %counter% + 1
    emote hands over a brass token.
    %load% obj 3300 %actor%
  done
  set trade_winter %actor.trade_winter%
  set curr_item %trade_winter.wordat(%traded_item%)%
  if %curr_item% == na
    set curr_item 1
  else
    eval curr_item %curr_item% + 1
  endif
  replace_word %curr_item% %traded_item% trade_winter
  remote trade_winter %actor.id%
  * Halt this script if we have successfully traded! 
  halt
endif

* Let's do the same for rejected items for special messages
* We are calling the same function 21627 again, but with
* rejected items as input
* So max_items is now rej_max_items, and prefix is rej

set max_items %rej_max_items%
set prefix rej
function 21627
set rej_traded_item %item_in_list%

if %rej_traded_item% != 0 
  eval tosay1 %%rejspeecha%rej_traded_item%%%
  eval tosay2 %%rejspeechb%rej_traded_item%%%
  %send% %actor% %tosay1%
  if %tosay2%
    %send% %actor% %tosay2%
  endif
  halt
else
  emote says, 'Sorry, I don't trade in that kind of stuff.'
endif

11
Suggestions & Ideas / Questjournal - Understanding and Debugging Quests
« on: January 05, 2017, 08:29:52 pm »
Hi all,

This post is meant to illustrate how to debug quest scripts with the questjournal. Writing triggers for the questjournal itself (and debugging them)will be in another post.

PS (sorry Roland, Thotter in advance) - feel free to add on to this post, clarify it if I skip some points.

questflags now look like this: questcardx_questname, for example, there's:

Code: [Select]
questcard7_viking
questcard19_riddle

To find all quest related triggers pertaining to that questcard, you can simply do
Code: [Select]
tsearch all questcardxx, example:
Code: [Select]
*find triggers related to questcard 19
tsearch all questcard19

All flags related to this quest will be in this flag. Sometimes, there may be subflags, eg
Code: [Select]
questcard19a
questcard19b
...

Most flags follow the below style. Here, we have a fake flag (questcard200_myquest), and show how it might look like, when you search for this flag on a player.

Code: [Select]
questcard200_myquest: noprog 0 notdone inprog 2 notdone inprog 3 done noprog 0 done
How do you read this? You can think of this in "groups of 3", so in this zone, there are four quests.

For the first quest, the player is not on the quest, is at step 0, and hasn't completed it.
For the second quest, the player is on the quest, is at step 2, and hasn't completed it.
For the third quest, the player is on the quest, is at step 3, and has completed the quest at least once.
For the fourth quest, the player has completed the quest, and hasn't restarted it again.

Think of noprog / inprog as noprogress and inprogress, notdone and done as whether the player has completed the quest at least once.

So we have two benefits already: First, flags are consolidated into one flag (less spam when you vstat a player's flags). Secondly, you can tsearch questflags easily by tsearch questcardxx.

However, because there are "multiple flags" on one flag (multiple variables in one array), things are a bit trickier.

Previously, you could do something like:
Code: [Select]
if !%actor.varexists(hanna)%
  * Player doesn't have this flag
  %send% %actor% You are not on this quest!
  %send% %actor% I will put you on this quest!
  set hanna 1
  remote hanna %actor.id%
else
  * Player has this flag
  set hanna %actor.hanna%
  if %hanna% == 1
    %send% %actor% You are on step 1. Let me progress you to step 2
    set hanna 2
    remote hanna %actor.id%
  elseif %hanna% == 2
    %send% %actor% You are on step 2. Let me progress you to step 3
    set hanna 2
    remote hanna %actor.id%
  endif
endif

How can you do this now? Let's imagine Hanna is the third quest for this zone, so we need to look at the third group, i.e. the group in bold here.

questcard200_myquest: noprog 0 notdone inprog 2 notdone inprog 3 done noprog 0 done

Let's look at a WRONG way to do this first.

Code: [Select]
* A wrong way
if !%actor.varexists(questcard200_myquest)%
  %send% %actor% You are not on this quest!
  %send% %actor% I will put you on this quest!
  set questcard200_myquest noprog 0 notdone noprog 0 notdone inprog 1 notdone noprog 0 notdone
  remote questcard200_myquest %actor.id%
endif

You might think the above code makes sense, since you're setting just the variables for the third quest. But, what if the player was on the other quests? You'd be resetting his or her progress.

Thus, every questcard now has up to three FUNCTIONS. A room function, a obj function, and a mob function. You can tsearch to find these functions, which look like this, and each have identical code (only the type - room, obj, mob differs).

Code: [Select]
* Example of an object function
Name: 'Questcard200 obj function',  VNum: [12345], RNum: [----]
Trigger Intended Assignment: Objects
Trigger Type: FUNCTION , Numeric Arg: 100, Arg list: None
Commands:
* Create this questflag if it doesn't exist
if !%actor.varexists(questcard200_quest)%
  set questcard200_quest noprog 0 notdone noprog 0 notdone noprog 0 notdone noprog 0 notdone
  remote questcard200_quest %actor.id%

  * To reassure people that their old flags aren't deleted
  * suppose they completed quest 1 before
  if %actor.varexists(oldflag1)%
    replace_word done 3 questcard200_quest
    rdelete oldflag1 %actor.id% 
  endif
 
  remote questcard200_quest %actor.id%
endif

Now, this is what you can do:
Code: [Select]
* Need to check if is PC before calling any functions.
if %actor.is_pc%
  *For any trigger which deals with questflags 
  *Call the appropriate function (room, mob or obj?)
  *Pretend it's an object so call the function above

  * This function sets the questflag ONCE, if it doesn't exist.
  function 12345
  * The below line can't bug, because we've created the flag if it didn't exist.
  set questcard200_quest %actor.questcard200_quest%

  * Now, proceed as normal, eg:

  if %questcard200_quest.wordat(7)% == noprog
    %send% %actor% You are not on this quest.
    %send% %actor% I will put you on this quest
    %send% %actor% I will replace the 7th word in your flag to show you are now in progress
    replace_word inprog 7 questcard200_quest
    %send% %actor% I will replace the 8th word by 1 to show you are now on step 1 for this quest
    replace_word 1 8 questcard200_quest
    remote questcard200_quest %actor.id%
  else
    if %questcard200_quest.wordat(8)% == 1
      %send% %actor% You are on step 1
      replace_word 2 8 questcard200_quest
      remote questcard200_quest %actor.id%
      %send% %actor% Now you are on step 2
    elseif %questcard200_quest.wordat(8)% == 2
      %send% %actor perfect, let's give you a reward
      if %questcard200_quest.wordat(9)% == notdone
        %send% %actor% You haven't done this quest before, so I'll give you a big reward!
        replace_word done 9 questcard200_quest
      else
        %send% %actor% You've done this quest before, so a smaller reward!
      endif
      * Reset all other questflags for this quest so
      replace_word 7 noprog questcard200_quest
      replace_word 8 0 questcard200_quest
      remote questcard200_quest %actor.id%
    endif
  endif
endif

So this is how the code would look like. Finally - how can we debug?

Here's some common errors.

Firstly, the trigger is not enclosed within:
Code: [Select]
if %actor.is_pc%
  ...
endif

Previously, you didn't need to have this check. Now you always need to have it.

Secondly, the WRONG type of function is called. We called function 12345, which was an Object function. If the trigger was a Mobile trigger, then you must call an Mobile function. If the trigger was a Room trigger, then you must call a Room function. Again, these three functions would have identical code, just different types.

Thirdly, logic wise. In this case, this is usually a problem with the old quest. As you can see, quests have the same logic. However, we have just used replace_word and wordat to work with arrays.



Finally, you may have a question. Hold on: How do I know if this player's quest is the THIRD quest or the FOURTH quest? Good question! Imms have a command (note me if you can't get this to work): questcheck card<num> debug <playername>, which actually goes into way more detail what each variable represents.





12
Suggestions & Ideas / Re: Questjournal v1
« on: December 19, 2016, 03:18:40 am »
*actually you know what - add me to the 4D Github if there's one. I'll also upload my trigger files for each quest. This makes it easy for everyone to debug.

13
Suggestions & Ideas / Re: Questjournal v1
« on: December 19, 2016, 03:11:41 am »
Back!

Here's the new plan - with Vryce, Jubei, and Mott helping.

The idea is to *rigorously* do and complete the quest journal. This also means extensively playtesting old quests.

Cycle will be slow, but this ensures no bugs.

14
Suggestions & Ideas / Re: Questjournal v1
« on: January 07, 2016, 01:04:25 am »
Update 7th January (or when Molly puts it in)

Two questcards added to the journal, card 41 and card 42. 

Under some circumstances (if you tried to do Quest 41 legitly), you would never be able to complete it. Now you can.

Infinite tomb robbers loading in some rooms (under some circumstances) has been fixed


15
Scripting Board / Re: Suggested Practices for Scripting
« on: January 05, 2016, 12:41:45 pm »
I've been using a template for the Quest Journal ; an example of a quest variable would be of the form:

Code: [Select]
hat_temple: noprog notdone 2
hat_temple: inprog notdone 1
hat_temple: noprog done 0

where noprog/inprog determines whether the player has activated / started the quest, notdone/done the "final" flag to signal completion, and the numbers to represent the steps in the quest. I'm hoping that (most) quests follow this template ; it's not as hard as it seems to set it up, since we just need a function to create this variable if it doesn't exist, so most scripts would be of the following form

Code: [Select]
* Create this variable if it hasn't existed
function 12345
set hat_temple %actor.hat_temple%

if %hat_temple.wordat(3)% == 2
  %echo% You are at step 2
endif

* Previously it would probably be something like
*if %actor.varexists(hat_temple_quest)%
* if %hat_temple_quest% == 2
*    %echo% You are at step 2
* endif
*endif
* and script errors would occur if you had
* if %actor.varexists(hat_temple_quest)% && %hat_temple_quest% == 2
* if the variable didn't exist

Of course, function 12345 would be

Code: [Select]
if !%actor.varexists(hat_temple)%
  set hat_temple noprog notdone 0
  remote hat_temple %actor.id%
endif

A side effect of this is that it removes errors where the script is something like
Code: [Select]
if %actor.varexists(hat_temple_quest)% && %hat_temple_quest% == 2
  %echo% do this
endif
and the player doesn't have the variable.

Pages: [1] 2 3 ... 21