Stendhal Quest Coding - Part 3: Difference between revisions

Jump to navigation Jump to search
Content deleted Content added
imported>Hendrik Brummermann
No edit summary
imported>Kribbel
m replace 2 old links
 
(111 intermediate revisions by 2 users not shown)
Line 1: Line 1:
{{Navigation for Stendhal Top}}
{{Navigation for Stendhal Top|Contributing}}
{{Navigation for Stendhal Contributors}}
{{Navigation for Stendhal Contributors}}
{{Stendhal Quests}}


__TOC__


{{Stendhal Quests}}


__TOC__
<div style="border: 3px solid green; background-color: #AFA; padding: 1em; margin-right: 20em">
This page is currently reworked. You can find the old content on the [[Talk:Stendhal Quest Coding|talk page]]
</div>


You may want to read the [[Stendhal Quest Coding|first part]] and [[Stendhal Quest Coding - Part 2|second part]] of the '''[[Stendhal Quest Coding]]''' tutorial first.
You may want to read the [[Stendhal Quest Coding|first part]] and [[Stendhal Quest Coding - Part 2|second part]] of the '''[[Stendhal Quest Coding]]''' tutorial first.
Line 17: Line 14:
== Rewarding the player ==
== Rewarding the player ==


In the last section of this tutorial we taught Hayunn to only accept one beer per player. We did neither care about taking the beer from the player nor did we reward the player. We want to add this functionality now.
In the last section of this tutorial we taught Hayunn to only accept one beer per player. We didn't actually take the beer from the player nor did we reward the player. We want to add this functionality now.


We have to do several things at once:
We have to do several things at once:
* take the beer from the player
* take the beer from the player
* provide some money as refund
* provide some money as refund
* increase the xp and karmy
* increase the xp and karma
* and finally remember that the quest was completed
* and finally remember that the quest was completed


But don't worry, that sounds more complicated than it actually is. There are already actions for all of these tasks that can be combined using a MultiAction:
But don't worry, that sounds more complicated than it actually is. There are already actions for all of these tasks that can be combined using a MultipleAction:


<source lang="java">
<source lang="java">
Line 45: Line 42:
== Asking the player for the beer ==
== Asking the player for the beer ==


Let's make it a bit easier for player to remember that they bought a beer to give it to Hayunn. Hayunn will notice the beer as soon as the player says "hi" and actively asks for it.
Let's make it a bit easier for player to remember that they bought a beer in order to give it to Hayunn. Hayunn should notice the beer as soon as the player says "hi" and he actively asks for it.


Therefore we add a new trigger for the GREETING_MESSAGE. There is an internal rule that transitions with have a condition (that is true) are preferred over equal transitions without one. So our greeting transition is used instead of the normal one if the condition is true. Similar to the MultiAction there is an AndCondition which combines multiple elementary ones.
Therefore we add a new trigger for the GREETING_MESSAGE. There is an internal rule that transitions which have a condition (that is true) are preferred over equal transitions without one. So our greeting transition is used instead of the normal one if the player has the item and the quest is active. Similar to the MultipleAction, there is an AndCondition which combines multiple elementary ones.


<source lang="java">
<source lang="java">

// player has the quest active and has a beer with him, ask for it
npc.add(
npc.add(
ConversationStates.IDLE,
ConversationStates.IDLE,
Line 58: Line 57:
null);
null);


// player has accepted the quest but did not bring a beer, remind him
npc.add(
npc.add(
ConversationStates.IDLE,
ConversationStates.IDLE,
Line 99: Line 99:
== Quest Documentation ==
== Quest Documentation ==


We are almost done now. Almost? Yes, a very important thing is still missing. And we should have done it as the first thing. But well, better late than never. Here we create the ''javadoc'' which summarises our code in a readable form, for other developers and for our future selves!
{{TODO|

* should be done much earlier usually
<source lang="java">
* content should be done earlier (see contributor's guide
/**
}}
* QUEST: Beer For Hayunn
*
* PARTICIPANTS:
* <ul>
* <li>Hayunn Naratha (the veteran warrior in Semos)</li>
* <li>Margaret (the tavern maid)</li>
* </ul>
*
* STEPS:
* <ul>
* <li>Hayunn asks you to buy a beer from Margaret.</li>
* <li>Margaret sells you a beer.</li>
* <li>Hayunn sees your beer, asks for it and then thanks you.</li>
* </ul>
*
* REWARD:
* <ul>
* <li>20 gold coins</li>
* <li>50 XP</li>
* <li>10 karma</li>
* </ul>
*
* REPETITIONS:
* <ul>
* <li>None</li>
* </ul>
*/
</source>

This documentation is only a little work in most cases because it is just a reformatted version of the [[Stendhal Quest Contribution#Refining_and_Discussing|quest description]].

== Quest Information Methods ==

These are used for the travel logs and other parts of the game which refer to quests - e.g. the achievement for completing all quests in Semos needs the region to be set for quests in Semos city, to be able to find them. These standard methods give meta information about the quest which is accessible to other parts of the Stendhal code.

=== getHistory ===
This fills in the quest history in the travel log. It is basically a list of steps in the quest in readable form. It starts with an empty list and then checks the state of the player. The list is added to if the player has completed that step. Finally the list is returned at the end.

<source lang = "java">
@Override
public List<String> getHistory(final Player player) {
final List<String> res = new ArrayList<String>();
if (!player.hasQuest(QUEST_SLOT)) {
return res;
}
res.add("I have talked to Hayunn.");
final String questState = player.getQuest(QUEST_SLOT);
if ("rejected".equals(questState)) {
res.add("I do not want to make Hayunn drunk.");
}
if (player.isQuestInState(QUEST_SLOT, "start", "done")) {
res.add("I promised to buy him a bear from Margaret in Semos Tavern.");
}
if (("start".equals(questState) && player.isEquipped("beer")) || "done".equals(questState)) {
res.add("I have a bottle of beer.");
}
if ("done".equals(questState)) {
res.add("I gave the beer to Hayunn. He paid me 20 gold coins and I got some experience.");
}
return res;
}
</source>

There are a few ways to check the states. Some developers use ChatConditions like we did when setting the NPC conversation. Others use more direct checks on the quest state. If using ChatConditions, these have been written to work with the add method for the NPC. If you just want to use them to get a true/false value directly, , you need to get at the 'fire', for example <code>(new() QuestCompletedCondition(QUEST_SLOT).fire(player, null, null)</code>.

=== getQuestInfo / fillQuestInfo ===
{{TODO|populate with an example from BeerForHayunn and explanation}}

=== getMinLevel ===
This piece of meta information suggests the minimum level of player who may be expected to completed the quest. It's not a hard requirement - ChatConditions on the level should be used if you really want to add a level restriction, when the NPC offers the quest.

This value is used in the travel log and for other NPCs who might hint that you start unstarted quests - they won't hint at unstarted quests beyond your level. If the value is 0, i.e. any player can do it, you don't need to add this method, as this is the default (though, BeerForHayunn does explicitly set the value to 0 and there is no harm in this.)

<source lang="java">
@Override
public int getMinLevel() {
return 10;
}
</source>

=== getRegion ===

If there is a specific region matching one of the Stendhal regions, as defined in [https://github.com/arianne/stendhal/blob/master/src/games/stendhal/server/maps/Region.java Region.java], then you can set this here and any achievements or NPCs referring to quests from that region will notice the quest. You can leave this method out if you have no region to set (e.g. global quests) because the default is null.

<source lang = "java">
@Override
public String getRegion() {
return Region.SEMOS_CITY;
}
</source>

=== getNPCName ===
Fill in the name of the NPC that the player starts the quest with. You can leave this method out if there is no specific NPC who starts the quest (e.g. the Seven Cherubim quest where you just start meeting any of the angels.) This is unlikely to happen often.
<source lang="java">
@Override
public String getNPCName() {
return "Hayunn Naratha";
}
</source>
Reference functions that gather 'meta' information about the quest use this - for example if an NPC wants to list which NPCs to speak to to start quests in a certain region.



{{TODO|Others? isCompleted? isRepeatable? More of the name ones?}}
== Advanced Techniques ==


== Further Reading and Complete Code ==
{{TODO|
* AlwaysTrueCondition
}}


You can have a look at the [https://github.com/arianne/stendhal/blob/master/src/games/stendhal/server/maps/quests/BeerForHayunn.java complete source code for this quest].
== Further Reading ==


The pages might be of interest to you:
{{TODO|
* [[HowToAddItemsStendhal|How to add items]]
}}
* [[HowToAddMapsServerStendhal|How to add new maps to server]]
* [[HowToAddMapsServerStendhal#Adding_NPC|How to create NPC]]
* [[How to test NPC Parser]] - ''this can help with trouble shooting if an NPC isn't understanding a response''
[[Category:Stendhal]]
[[Category:Stendhal]]