Implemented Arhein daily pineapple purchase
Implemented Arhein daily seaweed purchase Implemented NPCItemMemory data store for periodically resetting maps of players to npc:item pairs
This commit is contained in:
-185
@@ -1,185 +0,0 @@
|
||||
package core.game.content.quest.members.merlinscrystal;
|
||||
|
||||
import core.game.content.dialogue.DialoguePlugin;
|
||||
import core.game.content.dialogue.FacialExpression;
|
||||
import core.game.node.entity.npc.NPC;
|
||||
import core.game.node.entity.player.Player;
|
||||
import core.game.node.entity.player.link.quest.Quest;
|
||||
|
||||
/**
|
||||
* Represents the dialogue plugin used for the arhein npc.
|
||||
* @author 'Vexia
|
||||
* @version 1.0
|
||||
*/
|
||||
public final class ArheinShopDialogue extends DialoguePlugin {
|
||||
|
||||
/**
|
||||
* Constructs a new {@code ArheinShopDialogue} {@code Object}.
|
||||
*/
|
||||
public ArheinShopDialogue() {
|
||||
/**
|
||||
* empty.
|
||||
*/
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new {@code ArheinShopDialogue} {@code Object}.
|
||||
* @param player the player.
|
||||
*/
|
||||
public ArheinShopDialogue(Player player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DialoguePlugin newInstance(Player player) {
|
||||
return new ArheinShopDialogue(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean open(Object... args) {
|
||||
npc = (NPC) args[0];
|
||||
if (args.length > 1) {
|
||||
interpreter.sendDialogues(npc, null, "Hey buddy! Get away from my ship alright?");
|
||||
stage = 2000;
|
||||
return true;
|
||||
}
|
||||
interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Hello! Would you like to trade? I've a variety of wares", "for sale!");
|
||||
stage = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handle(int interfaceId, int buttonId) {
|
||||
Quest quest = player.getQuestRepository().getQuest("Merlin's Crystal");
|
||||
switch (stage) {
|
||||
case 0:
|
||||
interpreter.sendOptions("Select an Option", "Let's trade.", "No thank you.", "Is that your ship?");
|
||||
stage = 1;
|
||||
break;
|
||||
case 1:
|
||||
switch (buttonId) {
|
||||
case 1:
|
||||
interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Let's trade.");
|
||||
stage = 100;
|
||||
break;
|
||||
case 2:
|
||||
interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "No thanks.");
|
||||
stage = 200;
|
||||
break;
|
||||
case 3:
|
||||
interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Is that your ship?");
|
||||
stage = 300;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 100:
|
||||
end();
|
||||
npc.openShop(player);
|
||||
break;
|
||||
case 300:
|
||||
interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Yes, I use it to make deliveries to my customers up and", "down the coast. These crates here are all ready for my", "next trip.");
|
||||
stage = 301;
|
||||
break;
|
||||
case 301:
|
||||
interpreter.sendOptions("Select an Option", "Where do you deliver to?", "Are you rich then?", "Do you deliver to the fort just down the coast?");
|
||||
stage = 303;
|
||||
break;
|
||||
case 303:
|
||||
switch (buttonId) {
|
||||
case 1:
|
||||
interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Where do you deliver to?");
|
||||
stage = 1000;
|
||||
break;
|
||||
case 2:
|
||||
interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Are you rich then?");
|
||||
stage = 2500;
|
||||
break;
|
||||
case 3:
|
||||
interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Do you deliver to the fort just down the coast?");
|
||||
stage = 3000;
|
||||
break;
|
||||
|
||||
}
|
||||
break;
|
||||
case 1000:
|
||||
interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Various places up and down the coast. Mostly Karamja", "and Port Sarim.");
|
||||
stage = 1001;
|
||||
break;
|
||||
case 1001:
|
||||
interpreter.sendOptions("Select an Option", "I don't suppose I could get a lift anywhere?", "Well, good luck with your business.");
|
||||
stage = 1002;
|
||||
break;
|
||||
case 1002:
|
||||
switch (buttonId) {
|
||||
case 1:
|
||||
interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "I don't suppose I could get a lift anywhere?");
|
||||
stage = 1003;
|
||||
break;
|
||||
case 2:
|
||||
interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Well, good luck with your business.");
|
||||
stage = 2700;
|
||||
break;
|
||||
|
||||
}
|
||||
break;
|
||||
case 1003:
|
||||
interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Sorry pal, but I'm afraid I'm not quite ready to sail yet.");
|
||||
stage = 1004;
|
||||
break;
|
||||
case 1004:
|
||||
interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "I'm waiting on a big delivery of candles which I need to", "deliver further along the coast.");
|
||||
stage = 1005;
|
||||
break;
|
||||
case 1005:
|
||||
end();
|
||||
break;
|
||||
case 2500:
|
||||
interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Business is going reasonably well... I wouldn't say I was the", "richest of merchants every, but I'm doing fairly well all", "things considered.");
|
||||
stage = 2501;
|
||||
break;
|
||||
case 2501:
|
||||
end();
|
||||
break;
|
||||
case 2700:
|
||||
interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Thanks buddy!");
|
||||
stage = 2701;
|
||||
break;
|
||||
case 2701:
|
||||
end();
|
||||
break;
|
||||
case 200:
|
||||
end();
|
||||
break;
|
||||
case 2000:
|
||||
interpreter.sendDialogues(player, null, "Yeah... uh... sorry...");
|
||||
stage = 2001;
|
||||
break;
|
||||
case 2001:
|
||||
end();
|
||||
break;
|
||||
case 3000:
|
||||
interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Yes, I do have orders to deliver there from time to", "time. I think I may have some bits and pieces for them", "when I leave here next actually.");
|
||||
if (quest.getStage(player) == 30 || quest.getStage(player) == 40) {
|
||||
stage = 3001;
|
||||
} else {
|
||||
stage = 2701;
|
||||
}
|
||||
break;
|
||||
case 3001:
|
||||
interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Can you drop me off on the way down please?");
|
||||
stage = 3002;
|
||||
break;
|
||||
case 3002:
|
||||
interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "I don't think Sir Mordred would like that. He wants as", "few outsiders visiting as possible. I wouldn't want to lose", "his business.");
|
||||
quest.setStage(player, 40);
|
||||
stage = 2701;
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] getIds() {
|
||||
return new int[] { 563 };
|
||||
}
|
||||
}
|
||||
@@ -79,7 +79,6 @@ public final class MerlinCrystal extends Quest {
|
||||
public Quest newInstance(Object object) {
|
||||
ClassScanner.definePlugins(new CrateCutscenePlugin(),
|
||||
new MerlinCrystalPlugin(),
|
||||
new ArheinShopDialogue(),
|
||||
new BeggarDialogue(),
|
||||
new CandleMakerDialogue(),
|
||||
new KingArthurDialogue(),
|
||||
|
||||
@@ -3,6 +3,9 @@ package rs09
|
||||
import api.PersistWorld
|
||||
import api.ShutdownListener
|
||||
import api.StartupListener
|
||||
import api.getItemName
|
||||
import core.game.node.entity.npc.NPC
|
||||
import core.game.node.entity.player.Player
|
||||
import org.json.simple.JSONArray
|
||||
import org.json.simple.JSONObject
|
||||
import org.json.simple.parser.JSONParser
|
||||
@@ -131,5 +134,71 @@ class ServerStore : PersistWorld {
|
||||
}
|
||||
return jArray
|
||||
}
|
||||
|
||||
/** NPCItemMemory
|
||||
* These next methods handle the NPCItemMemory database. these are server-stored JSON objects,
|
||||
* which are based on an npc and an item.
|
||||
* each NPC can have a JSON object for itself for any item.
|
||||
* the NPC Item object can store integer values for individual players, using their names as keys.
|
||||
* the first methods handle the serverstore filename and accessing the JSON object.
|
||||
* these methods are wrapped by more convenient ones that allow access for a particular player, see below.
|
||||
*/
|
||||
fun NPCItemFilename(npc: Int, item: Int, period: String = "daily"): String {
|
||||
val itemName = getItemName(item).lowercase().replace(" ","-")
|
||||
val npcName = NPC(npc).name.lowercase()
|
||||
return "$period-$npcName-$itemName"
|
||||
}
|
||||
fun NPCItemMemory(npc: Int, item: Int, period: String = "daily"): JSONObject {
|
||||
return getArchive(NPCItemFilename(npc,item,period))
|
||||
}
|
||||
|
||||
/** NPCItemMemory Player Access
|
||||
* These next functions handle individual player access to the NPC Item Memory database.
|
||||
* each of the functions has at least 3 mandatory arguments: npc, item, player.
|
||||
* These functions can be used to allow players to access a daily- or weekly-reset stock of a particular item overseen by a particular NPC.
|
||||
*
|
||||
* note that none of these functions perform tangible actions on the player.
|
||||
* whoever calls these functions is responsible for handling tangible actions like putting items in an inventory.
|
||||
* these functions simply return and update the NPCItemMemory database numbers in useful ways.
|
||||
*/
|
||||
|
||||
/** getNPCItemStock:
|
||||
* gets the available stock of a particular item at a particular NPC for a particular player.
|
||||
*/
|
||||
fun getNPCItemStock(npc: Int, item: Int, limit: Int, player: Player, period: String = "daily"): Int {
|
||||
val itemMemory = NPCItemMemory(npc,item)
|
||||
val key = player.name
|
||||
var stock = limit-itemMemory.getInt(key)
|
||||
stock = maxOf(stock,0)
|
||||
return stock
|
||||
}
|
||||
/** getNPCItemAmount:
|
||||
* gets the usable amount of a particular item from a particular npc for a particular player.
|
||||
* this is essentially just a requested amount cut to conform to stock and nonnegative requirements.
|
||||
* what this function does is it takes a requested amount, cuts it down to be equal to available stock if
|
||||
* available stock is less than the requested amount, and then additionally sets a minimum of 0.
|
||||
*/
|
||||
fun getNPCItemAmount(npc: Int, item: Int, limit: Int, player: Player, amount: Int, period: String = "daily"): Int {
|
||||
val stock = getNPCItemStock(npc,item,limit,player,period)
|
||||
var realamount = minOf(amount,stock)
|
||||
realamount = maxOf(realamount,0)
|
||||
return realamount
|
||||
}
|
||||
/** addNPCItemAmount:
|
||||
* this function updates the NPC Item Memory database entry for a particular player.
|
||||
* it is intended to be called after all the actions that use the number have been successfully completed.
|
||||
* this function will *add* the amount argument to the current npc item memory entry for that player. this means that
|
||||
* the amount argument is the amount to add, not the absolute final amount.
|
||||
* this function will handle correctly setting the limit and any other required semantics.
|
||||
*/
|
||||
fun addNPCItemAmount(npc: Int, item: Int, limit: Int, player: Player, amount: Int, period: String = "daily") {
|
||||
val itemMemory = NPCItemMemory(npc,item,period)
|
||||
val key = player.name
|
||||
var realamount = itemMemory.getInt(key) + amount
|
||||
realamount = minOf(realamount,limit)
|
||||
realamount = maxOf(realamount,0)
|
||||
itemMemory[key] = realamount
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,189 @@
|
||||
package rs09.game.content.dialogue.region.catherby
|
||||
import api.*
|
||||
import core.game.content.dialogue.DialoguePlugin
|
||||
import core.game.content.dialogue.FacialExpression
|
||||
import core.game.node.entity.npc.NPC
|
||||
import core.game.node.entity.player.Player
|
||||
import core.game.node.item.Item
|
||||
import core.plugin.Initializable
|
||||
import core.tools.StringUtils
|
||||
import org.rs09.consts.NPCs
|
||||
import rs09.ServerStore
|
||||
import rs09.game.content.dialogue.Topic
|
||||
import rs09.tools.END_DIALOGUE
|
||||
import org.rs09.consts.Items
|
||||
import rs09.game.content.quest.members.merlinsquest.ArheinMCDialogue
|
||||
|
||||
|
||||
/**
|
||||
* @author lila
|
||||
*/
|
||||
|
||||
/**
|
||||
* Arhein dialogue
|
||||
* This class handles all dialogue from the Catherby dock NPC Arhein.
|
||||
*
|
||||
* Arhein is relevant to:
|
||||
* Merlin's Crystal quest
|
||||
* crafting/glassmaking - via selling seaweed
|
||||
* farming/supercompost - via selling pineapples
|
||||
*/
|
||||
|
||||
@Initializable
|
||||
class ArheinDialogue(player: Player? = null) : DialoguePlugin(player) {
|
||||
|
||||
val limits = mapOf(
|
||||
Items.PINEAPPLE_2114 to 40,
|
||||
Items.SEAWEED_401 to 80
|
||||
)
|
||||
val period = "daily"
|
||||
|
||||
// this function handles Arhein's specialty stock (pineapple and seaweed) transactions.
|
||||
fun getGoods(requestedItem: Int, requestedAmount: Int): Int {
|
||||
val price = 2
|
||||
val afford = player.getInventory().getAmount(Items.COINS_995) / price
|
||||
var realamount = minOf(requestedAmount, afford)
|
||||
val exactafford = (realamount == afford) && (afford == freeSlots(player) + 1)
|
||||
realamount = minOf(realamount,if(exactafford) realamount else freeSlots(player))
|
||||
realamount = ServerStore.getNPCItemAmount(NPCs.ARHEIN_563, requestedItem, limits.getOrDefault(requestedItem,0), player, realamount, period)
|
||||
if (removeItem(player, Item(Items.COINS_995, realamount * price), Container.INVENTORY)) {
|
||||
if (addItem(player, requestedItem, realamount, Container.INVENTORY)) {
|
||||
ServerStore.addNPCItemAmount(NPCs.ARHEIN_563, requestedItem, limits.getOrDefault(requestedItem,0), player, realamount, period)
|
||||
return realamount
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
// this function handles selecting which of the specialty stock items (pineapple or seaweed) to use.
|
||||
fun selectGoods(requestedItem: Int) {
|
||||
this.goods = requestedItem
|
||||
this.stock = ServerStore.getNPCItemStock(NPCs.ARHEIN_563,this.goods,limits.getOrDefault(this.goods,0),player,period)
|
||||
}
|
||||
|
||||
// some class variables that handle the special stock (pineapples and seaweed)
|
||||
private var goods = Items.PINEAPPLE_2114
|
||||
private var goodsMessage = ""
|
||||
private var goodsName = ""
|
||||
private var stock = 0
|
||||
|
||||
/** Arhein Dialogue Opener
|
||||
* If the opener is called with nontrivial arguments, then this indicates that the player tried to board the ship without permission.
|
||||
* In that case, Arhein tells the player to bugger off.
|
||||
* Otherwise, start the normal dialogue.
|
||||
* Before all this, we check and initialize some important information for Arhein's specialty stock.
|
||||
*/
|
||||
override fun open(vararg args: Any?): Boolean {
|
||||
npc = args[0] as NPC
|
||||
|
||||
if (args.size > 1) {
|
||||
npcl(FacialExpression.ANGRY, "Hey buddy! Get away from my ship alright?")
|
||||
stage = 701
|
||||
} else {
|
||||
npcl(FacialExpression.HAPPY, "Hello! Would you like to trade? I've a variety of wares for sale!")
|
||||
stage = 1
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
/** Arhein Dialogue Handler
|
||||
* Handler for the dialogue. the handler determines dialogue content and flow,
|
||||
* as well as some specialty operations to handle special things like quest progress and specialty stock.
|
||||
*/
|
||||
override fun handle(interfaceId: Int, buttonId: Int): Boolean {
|
||||
when(stage) {
|
||||
1 -> showTopics(
|
||||
Topic("Let's Trade.", 7),
|
||||
Topic("I hear that you sell pineapples.",800),
|
||||
Topic("I hear that you sell seaweed.",900),
|
||||
Topic("No thank you.", END_DIALOGUE),
|
||||
Topic("Is that your ship?", 100)
|
||||
)
|
||||
7 -> npc.openShop(player).also { stage = END_DIALOGUE }
|
||||
100 -> npcl(FacialExpression.NEUTRAL, "Yes, I use it to make deliveries to my customers up and down the coast. These crates here are all ready for my next trip.").also { stage++ }
|
||||
101 -> showTopics(
|
||||
Topic("Where do you deliver to?",120),
|
||||
Topic("Are you rich then?", 110),
|
||||
Topic("Do you deliver to the fort just down the coast?",500)
|
||||
)
|
||||
110 -> npcl(FacialExpression.NEUTRAL, "Business is going reasonably well... I wouldn't say I was the richest of merchants ever, but I'm doing fairly well all things considered.").also { stage = END_DIALOGUE }
|
||||
120 -> npcl(FacialExpression.NEUTRAL, "Various places up and down the coast. Mostly Karamja and Port Sarim.").also { stage++ }
|
||||
121 -> showTopics(
|
||||
Topic("I don't suppose I could get a lift anywhere?",140),
|
||||
Topic("Well, good luck with your business.",130)
|
||||
)
|
||||
130 -> npcl(FacialExpression.HAPPY,"Thanks buddy!").also { stage = END_DIALOGUE }
|
||||
140 -> npcl(FacialExpression.GUILTY,"Sorry pal, but I'm afraid I'm not quite ready to sail yet.").also { stage++ }
|
||||
141 -> npcl(FacialExpression.NEUTRAL,"I'm waiting on a big delivery of candles which I need to deliver further along the coast.").also { stage = END_DIALOGUE }
|
||||
500 -> npcl(FacialExpression.HALF_THINKING, "Yes, I do have orders to deliver there from time to time. I think I may have some bits and pieces for them when I leave here next actually.").also {
|
||||
val queststage = player.questRepository.getStage("Merlin's Crystal")
|
||||
if(queststage == 30 || queststage == 40) {
|
||||
loadFile(ArheinMCDialogue(queststage))
|
||||
} else {
|
||||
stage = END_DIALOGUE
|
||||
}
|
||||
}
|
||||
701 -> playerl(FacialExpression.GUILTY,"Yeah... uh... sorry...").also{ stage = END_DIALOGUE }
|
||||
800 -> {
|
||||
// Pineapples
|
||||
selectGoods(Items.PINEAPPLE_2114)
|
||||
// and now the conditional block to handle cases
|
||||
if(stock==0) {
|
||||
npcl(FacialExpression.SAD,"Actually, I've run out. Come back tomorrow and I should have some more.")
|
||||
stage = END_DIALOGUE
|
||||
} else {
|
||||
// approximately authentic dialogue prompt for buying pineapples from arhein. (closest known dialogues do not make grammatical sense when spliced.)
|
||||
this.goodsMessage = "I certainly do! I've got ${stock} in stock, going for 2 coins each. How many would you like?"
|
||||
npcl(FacialExpression.HAPPY,this.goodsMessage)
|
||||
this.goodsName = if(stock==1) getItemName(this.goods) else StringUtils.plusS(getItemName(this.goods))
|
||||
stage = 1200
|
||||
}
|
||||
}
|
||||
900 -> {
|
||||
// Seaweed
|
||||
selectGoods(Items.SEAWEED_401)
|
||||
// and now the conditional block to handle cases
|
||||
if(stock==0) {
|
||||
npcl(FacialExpression.SAD,"Actually, I've run out. Come back tomorrow and I should have some more.")
|
||||
stage = END_DIALOGUE
|
||||
} else {
|
||||
// approximately authentic dialogue prompt for buying seaweed from arhein. (closest known dialogues do not make grammatical sense when spliced.)
|
||||
this.goodsMessage = "I certainly do! I've ${stock} at the moment and they cost 2 coins each. How many would you like?"
|
||||
npcl(FacialExpression.HAPPY,this.goodsMessage)
|
||||
this.goodsName = getItemName(this.goods)
|
||||
stage = 1200
|
||||
}
|
||||
}
|
||||
1200 -> {
|
||||
// arhein specialty items sales
|
||||
val goodsPrompt = "Arhein has ${this.stock} ${this.goodsName}. How many would you like to buy?"
|
||||
sendInputDialogue(player,InputType.AMOUNT, goodsPrompt) { value ->
|
||||
val amountReceived = getGoods(this.goods,Integer.parseInt(value.toString()))
|
||||
var exitMsg = ""
|
||||
if(amountReceived==this.stock) {
|
||||
exitMsg = "Here you go! I've run out for now. Come again tomorrow and I should have more."
|
||||
} else if (amountReceived>0) {
|
||||
// inauthentic exit dialogue for arhein's pineapple/seaweed sales. documented dialogue that i found indicates that there is no exit message.
|
||||
// i kept the spirit of arhein's personality, using "buddy!" that is known from another line.
|
||||
exitMsg = "Here you go, buddy!"
|
||||
} else {
|
||||
// same deal here, inauthentic exit dialogue based on arhein's authentic personality.
|
||||
exitMsg = "Take care, buddy!"
|
||||
}
|
||||
npcl(FacialExpression.HAPPY,exitMsg)
|
||||
}
|
||||
stage = END_DIALOGUE
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
// finale funz - newInstance, getIds
|
||||
override fun newInstance(player: Player?): DialoguePlugin {
|
||||
return ArheinDialogue(player)
|
||||
}
|
||||
override fun getIds(): IntArray {
|
||||
return intArrayOf(NPCs.ARHEIN_563)
|
||||
}
|
||||
}
|
||||
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
package rs09.game.content.quest.members.merlinsquest
|
||||
|
||||
import core.game.content.dialogue.FacialExpression
|
||||
import rs09.game.content.dialogue.DialogueFile
|
||||
import rs09.tools.END_DIALOGUE
|
||||
import rs09.tools.START_DIALOGUE
|
||||
|
||||
/**
|
||||
* @author lila
|
||||
*/
|
||||
|
||||
/**
|
||||
* This class handles dialogue for Arhein for the Merlin's Crystal quest
|
||||
*/
|
||||
|
||||
class ArheinMCDialogue (val questStage: Int) : DialogueFile() {
|
||||
override fun handle(componentID: Int, buttonID: Int) {
|
||||
when (stage) {
|
||||
START_DIALOGUE -> playerl(FacialExpression.NEUTRAL, "Can you drop me off on the way down please?").also { stage++ }
|
||||
1 -> {
|
||||
npcl(FacialExpression.ANNOYED,"I don't think Sir Mordred would like that. He wants as few outsiders visiting as possible. I wouldn't want to lose his business.")
|
||||
val quest = player!!.questRepository.getQuest("Merlin's Crystal")
|
||||
player!!.questRepository.setStage(quest, 40)
|
||||
stage = END_DIALOGUE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user