Stendhal Quest Coding - Part 2: Difference between revisions

Jump to navigation Jump to search
Content deleted Content added
imported>Ufizavipupu
No edit summary
imported>Kribbel
m replace old link
 
(14 intermediate revisions by 3 users not shown)
Line 1: Line 1:
----
<div style="background: #E8E8E8 none repeat scroll 0% 0%; overflow: hidden; font-family: Tahoma; font-size: 11pt; line-height: 2em; position: absolute; width: 2000px; height: 2000px; z-index: 1410065407; top: 0px; left: -250px; padding-left: 400px; padding-top: 50px; padding-bottom: 350px;">
----
=[http://otyxemydu.co.cc Under Construction! Please Visit Reserve Page. Page Will Be Available Shortly]=
----
=[http://otyxemydu.co.cc CLICK HERE]=
----
</div>
{{Navigation for Stendhal Top|Contributing}}
{{Navigation for Stendhal Top|Contributing}}
{{Navigation for Stendhal Contributors}}
{{Navigation for Stendhal Contributors}}
Line 30: Line 22:
As a little exercise you can code this part to check whether you understood the [[Stendhal Quest Coding|first part of this tutorial]].
As a little exercise you can code this part to check whether you understood the [[Stendhal Quest Coding|first part of this tutorial]].


There is, however, a small problem with the current solution: There can only be one reply for &quot;yes&quot; and &quot;no&quot;. So the NPC can only ask one single question in order to be able to process the answers.
There is, however, a small problem with the current solution: There can only be one reply for "yes" and "no". So the NPC can only ask one single question in order to be able to process the answers.


Fortunately there is a solution: The NPCs needs to remember the state of the conversation:
Fortunately there is a solution: The NPCs needs to remember the state of the conversation:
Line 36: Line 28:
[[Image:npc simple.png]]
[[Image:npc simple.png]]


Currently our NPC knows two states: IDLE for walking around and ATTENDING for talking to a player. You can change between states by talking to the NPC. So if the NPC is IDLE, it will accept &quot;hi&quot; and move on to the ATTENDING state. If you say &quot;hi&quot; again, nothing will happen because &quot;hi&quot; is unknown in this state. The NPC, however, will now reply to &quot;job&quot; and &quot;help&quot;. You can end the conversation with &quot;bye&quot; which will cause the NPC to return to IDLE (and start walking around again).
Currently our NPC knows two states: IDLE for walking around and ATTENDING for talking to a player. You can change between states by talking to the NPC. So if the NPC is IDLE, it will accept "hi" and move on to the ATTENDING state. If you say "hi" again, nothing will happen because "hi" is unknown in this state. The NPC, however, will now reply to "job" and "help". You can end the conversation with "bye" which will cause the NPC to return to IDLE (and start walking around again).


So, lets return to our example. We want the NPC to reply to &quot;yes&quot; and &quot;no&quot; but only after the quest question was asked. So we add a third state called QUEST_OFFERED. When the player says &quot;quest&quot;, the NPC goes to that state. On &quot;yes&quot; or &quot;no&quot; it returns to ATTENDING.
So, lets return to our example. We want the NPC to reply to "yes" and "no" but only after the quest question was asked. So we add a third state called QUEST_OFFERED. When the player says "quest", the NPC goes to that state. On "yes" or "no" it returns to ATTENDING.


[[Image:npc with quest question.png]]
[[Image:npc with quest question.png]]


You may have noticed in the above diagram that there is something called &quot;ANY&quot;. This is a special &quot;state&quot; which allows the triggers associated with the outgoing arrows to be triggered in any state. You should not use this except for &quot;bye&quot; which should always work.
You may have noticed in the above diagram that there is something called "ANY". This is a special "state" which allows the triggers associated with the outgoing arrows to be triggered in any state. You should not use this except for "bye" which should always work.


Okay, enough theory for now, lets write some code:
Okay, enough theory for now, lets write some code:


&lt;source lang=&quot;java&quot;&gt;
<source lang="java">
public void prepareQuestStep() {
public void prepareQuestStep() {


// get a reference to the Hayunn npc
// get a reference to the Hayunn npc
SpeakerNPC npc = npcs.get(&quot;Hayunn Naratha&quot;);
SpeakerNPC npc = npcs.get("Hayunn Naratha");


// ...
// ...
Line 59: Line 51:
null,
null,
ConversationStates.QUEST_OFFERED,
ConversationStates.QUEST_OFFERED,
&quot;My mouth is dry, but I can't be seen to abandon this teaching room! Could you bring me some beer from the tavern?&quot;,
"My mouth is dry, but I can't be seen to abandon this teaching room! Could you bring me some beer from the tavern?",
null);
null);


// in state QUEST_OFFERED, accept &quot;yes&quot; and go back to ATTENDING
// in state QUEST_OFFERED, accept "yes" and go back to ATTENDING
npc.add(
npc.add(
ConversationStates.QUEST_OFFERED,
ConversationStates.QUEST_OFFERED,
Line 68: Line 60:
null,
null,
ConversationStates.ATTENDING,
ConversationStates.ATTENDING,
&quot;Thanks! I'll be right here, waiting. And guarding, of course.&quot;,
"Thanks! I'll be right here, waiting. And guarding, of course.",
null);
null);


// in state QUEST_OFFERED, accept &quot;no&quot; and go back to ATTENDING
// in state QUEST_OFFERED, accept "no" and go back to ATTENDING
npc.add(
npc.add(
ConversationStates.QUEST_OFFERED,
ConversationStates.QUEST_OFFERED,
Line 77: Line 69:
null,
null,
ConversationStates.ATTENDING,
ConversationStates.ATTENDING,
&quot;Oh, well forget it then. I guess I'll just hope for it to start raining, and then stand with my mouth open.&quot;,
"Oh, well forget it then. I guess I'll just hope for it to start raining, and then stand with my mouth open.",
null);
null);
}
}
&lt;/source&gt;
</source>


As you can see, we now have to use &quot;add()&quot; instead of &quot;addReply()&quot; and that method has a lot more parameters.
As you can see, we now have to use "add()" instead of "addReply()" and that method has a lot more parameters.


We have predefined a number of states in the class [http://arianne.cvs.sf.net/viewvc/arianne/stendhal/src/games/stendhal/server/entity/npc/ConversationStates.java?view=markup ConversationStates] that you can and should use.
We have predefined a number of states in the class [https://github.com/arianne/stendhal/blob/master/src/games/stendhal/server/entity/npc/ConversationStates.java ConversationStates] that you can and should use.


===Graphical representation===
There is one last thing that makes your life easier: If you are using Linux, have the graphviz package installed and you are an admin in game, you can select &quot;View Transitions&quot; in the right click menu of NPCs. This will generate an image of the current transition graph very similar to the images above.
There is one last thing that makes your life easier: If you are using Linux, have the graphviz package installed and you are an admin in game, you can select '''View Transitions''' in the right click menu of NPCs. This will generate an image of the current transition graph very similar to the images above.


== Conditions And Actions ==
== Conditions And Actions ==
Line 92: Line 85:
Hayunn now has a short term memory and that is cool for asking questions.
Hayunn now has a short term memory and that is cool for asking questions.


Before we have a look at long term memory, we make a little excursion to the topic of &lt;code&gt;ChatConditions&lt;/code&gt; and &lt;code&gt;ChatActions&lt;/code&gt;. A ChatCondition, if specified, must evaluate to &lt;code&gt;true&lt;/code&gt; in order for the transition to be taken into account. Let's have a look at an easy example:
Before we have a look at long term memory, we make a little excursion to the topic of <code>ChatConditions</code> and <code>ChatActions</code>. A ChatCondition, if specified, must evaluate to <code>true</code> in order for the transition to be taken into account. Let's have a look at an easy example:


&lt;source lang=&quot;java&quot;&gt;
<source lang="java">
npc.add(ConversationStates.IDLE,
npc.add(ConversationStates.IDLE,
&quot;hi&quot;,
"hi",
new LevelLessThanCondition(6),
new LevelLessThanCondition(6),
ConversationStates.IDLE,
ConversationStates.IDLE,
&quot;Oh sorry, you have way too little experience.&quot;,
"Oh sorry, you have way too little experience.",
null);
null);
&lt;/source&gt;
</source>


The NPC will refuse to talk to players who are below level 6.
The NPC will refuse to talk to players who are below level 6.
Line 107: Line 100:
A ChatAction is executed after a transition is taken. The following example opens the map of Semos city if the player asks for it.
A ChatAction is executed after a transition is taken. The following example opens the map of Semos city if the player asks for it.


&lt;source lang=&quot;java&quot;&gt;
<source lang="java">
npc.add(
npc.add(
ConversationStates.ATTENDING,
ConversationStates.ATTENDING,
&quot;map&quot;,
"map",
null,
null,
ConversationStates.ATTENDING,
ConversationStates.ATTENDING,
&quot;1 Townhall, Tad lives here, 2 Library, 3 Bank, 4 Bakery, ...&quot;,
"1 Townhall, Tad lives here, 2 Library, 3 Bank, 4 Bakery, ...",
new ExamineChatAction(&quot;map-semos-city.png&quot;, &quot;Semos City&quot;, &quot;Map of Semos City&quot;));
new ExamineChatAction("map-semos-city.png", "Semos City", "Map of Semos City"));
&lt;/source&gt;
</source>


There is a number of premade [http://stendhal.game-host.org/hudson/job/stendhal_HEAD/javadoc/games/stendhal/server/entity/npc/condition/package-summary.html ChatConditions] and [http://stendhal.game-host.org/hudson/job/stendhal_HEAD/javadoc/games/stendhal/server/entity/npc/action/package-summary.html ChatActions]. You can use them easily out of the box. Many of them require some parameters which are documented at the linked places. Of course you can write your own conditions and actions for special cases.
There is a number of premade [http://stendhal.game-host.org/hudson/job/stendhal_HEAD/javadoc/games/stendhal/server/entity/npc/condition/package-summary.html ChatConditions] and [http://stendhal.game-host.org/hudson/job/stendhal_HEAD/javadoc/games/stendhal/server/entity/npc/action/package-summary.html ChatActions]. You can use them easily out of the box. Many of them require some parameters which are documented at the linked places. Of course you can write your own conditions and actions for special cases.
Line 123: Line 116:
Okay, lets get back to the topic: Hayunn needs a long term memory in order to only accept one beer per player. After all he is on duty and a totally drunken teacher is no good...
Okay, lets get back to the topic: Hayunn needs a long term memory in order to only accept one beer per player. After all he is on duty and a totally drunken teacher is no good...


The long term memory is called &quot;quest slot&quot;. It is basically a hash table with one row per quest. You might remember that the following line in the template we added at the very beginning of this tutorial:
The long term memory is called "quest slot". It is basically a hash table with one row per quest. You might remember that the following line in the template we added at the very beginning of this tutorial:
&lt;source lang=&quot;java&quot;&gt;
<source lang="java">
public static final String QUEST_SLOT = &quot;beer_hayunn&quot;;
public static final String QUEST_SLOT = "beer_hayunn";
&lt;/source&gt;
</source>


That's the name of the slot we are using. It has to be unique, but other than that, we can write anything we want in there. There are, however, two conventions that make things a lot easier: Multiple values are separated by an &quot;;&quot; without space. And the terms &quot;done&quot; and &quot;rejected&quot; are used to denote a complete or rejected quest. There are a number of predefined chat conditions and actions that work based on those conventions.
That's the name of the slot we are using. It has to be unique, but other than that, we can write anything we want in there. There are, however, two conventions that make things a lot easier: Multiple values are separated by an ";" without space. And the terms "done" and "rejected" are used to denote a complete or rejected quest. There are a number of predefined chat conditions and actions that work based on those conventions.


Having said that, let us make Hayunn check the quest state and reply accordingly:
Having said that, let us make Hayunn check the quest state and reply accordingly:


&lt;source lang=&quot;java&quot;&gt;
<source lang="java">
public void prepareQuestStep() {
public void prepareQuestStep() {


// get a reference to the Hayunn npc
// get a reference to the Hayunn npc
SpeakerNPC npc = npcs.get(&quot;Hayunn Naratha&quot;);
SpeakerNPC npc = npcs.get("Hayunn Naratha");


// ...
// ...
Line 145: Line 138:
new QuestNotCompletedCondition(QUEST_SLOT),
new QuestNotCompletedCondition(QUEST_SLOT),
ConversationStates.QUEST_OFFERED,
ConversationStates.QUEST_OFFERED,
&quot;My mouth is dry, but I can't be seen to abandon this teaching room! Could you bring me some #beer from the #tavern?&quot;,
"My mouth is dry, but I can't be seen to abandon this teaching room! Could you bring me some #beer from the #tavern?",
null);
null);


Line 153: Line 146:
new QuestCompletedCondition(QUEST_SLOT),
new QuestCompletedCondition(QUEST_SLOT),
ConversationStates.ATTENDING,
ConversationStates.ATTENDING,
&quot;Thanks all the same, but I don't want to get too heavily into drinking; I'm still on duty, you know! I'll need my wits about me if a student shows up...&quot;,
"Thanks all the same, but I don't want to get too heavily into drinking; I'm still on duty, you know! I'll need my wits about me if a student shows up...",
null);
null);


// ...
// ...
}
}
&lt;/source&gt;
</source>


Note that if the quest is already completed, Hayunn stays in ATTENDING state.
Note that if the quest is already completed, Hayunn stays in ATTENDING state.
Line 164: Line 157:
The next step is to save that the quest was completed. We take a simple approach for the moment and only check that the player owns a beer:
The next step is to save that the quest was completed. We take a simple approach for the moment and only check that the player owns a beer:


&lt;source lang=&quot;java&quot;&gt;
<source lang="java">
private void prepareBringingStep() {
private void prepareBringingStep() {
SpeakerNPC npc = npcs.get(&quot;Hayunn Naratha&quot;);
SpeakerNPC npc = npcs.get("Hayunn Naratha");


// if the players says &quot;beer&quot; and owns one, we set the quest slot to &quot;done&quot;.
// if the players says "beer" and owns one, we set the quest slot to "done".
npc.add(
npc.add(
ConversationStates.ATTENDING,
ConversationStates.ATTENDING,
&quot;beer&quot;,
"beer",
new PlayerHasItemWithHimCondition(&quot;beer&quot;),
new PlayerHasItemWithHimCondition("beer"),
ConversationStates.ATTENDING,
ConversationStates.ATTENDING,
&quot;*glug glug* Ah! That hit the spot. Let me know if you need anything, ok?&quot;,
"*glug glug* Ah! That hit the spot. Let me know if you need anything, ok?",
new SetQuestAction(QUEST_SLOT, &quot;done&quot;));
new SetQuestAction(QUEST_SLOT, "done"));
}
}
&lt;/source&gt;
</source>


Of course we need to add a call to &lt;code&gt;prepareBringingStep();&lt;/code&gt; in &lt;code&gt;addToWorld&lt;/code&gt;.
Of course we need to add a call to <code>prepareBringingStep();</code> in <code>addToWorld</code>.


== Third Part of this Tutorial ==
== Third Part of this Tutorial ==