From 7b2d4070cddfac478df6a9811f2361ffa26f2c3a Mon Sep 17 00:00:00 2001 From: Jordan Irwin Date: Sun, 9 Jun 2024 10:03:53 -0700 Subject: [PATCH 1/4] Add hasOwner method to item super class --- src/games/stendhal/server/entity/item/Item.java | 10 ++++++++++ src/games/stendhal/server/entity/item/OwnedItem.java | 8 ++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/games/stendhal/server/entity/item/Item.java b/src/games/stendhal/server/entity/item/Item.java index ff75a6d34b3..5a8af7f3393 100644 --- a/src/games/stendhal/server/entity/item/Item.java +++ b/src/games/stendhal/server/entity/item/Item.java @@ -565,6 +565,16 @@ public void autobind(String player) { } } + /** + * Checks if an item is marked as "owned" by a specific player. + * + * @return + * Always {@code false} in this implementation. + */ + public boolean hasOwner() { + return false; + } + /** * Get the item's itemData. The itemData contains context specific * information that is used by the implementation. diff --git a/src/games/stendhal/server/entity/item/OwnedItem.java b/src/games/stendhal/server/entity/item/OwnedItem.java index 5a843a5f46d..a99b86fea1b 100644 --- a/src/games/stendhal/server/entity/item/OwnedItem.java +++ b/src/games/stendhal/server/entity/item/OwnedItem.java @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright © 2020 - Arianne * + * Copyright © 2020-2024 - Faiumoni e. V. * *************************************************************************** *************************************************************************** * * @@ -21,6 +21,9 @@ /** * Class representing an item owned by an entity. + * + * An "owned" item is different than a bound item in that it can be used by players other than the + * owner but they do not get the same or full benefit of. */ public abstract class OwnedItem extends Item { @@ -82,8 +85,9 @@ public boolean onUsed(final RPEntity user) { * Override to check if item has owner. * * @return - * true if owned. + * {@ true} if owned. */ + @Override public boolean hasOwner() { return getOwner() != null; } From 90b49fa819398c981598e7e2e7959db049f1acfe Mon Sep 17 00:00:00 2001 From: Jordan Irwin Date: Sun, 9 Jun 2024 10:04:27 -0700 Subject: [PATCH 2/4] Player slot for managing item correspondence between players --- .../server/entity/item/OwnedItem.java | 2 +- .../server/entity/player/PlayerRPClass.java | 5 +- .../stendhal/server/entity/slot/Mailbox.java | 182 ++++++++++++++++++ 3 files changed, 187 insertions(+), 2 deletions(-) create mode 100644 src/games/stendhal/server/entity/slot/Mailbox.java diff --git a/src/games/stendhal/server/entity/item/OwnedItem.java b/src/games/stendhal/server/entity/item/OwnedItem.java index a99b86fea1b..b260de4bcba 100644 --- a/src/games/stendhal/server/entity/item/OwnedItem.java +++ b/src/games/stendhal/server/entity/item/OwnedItem.java @@ -28,7 +28,7 @@ public abstract class OwnedItem extends Item { // slots to which item cannot be equipped if it has an owner - private List ownedBlacklistSlots = Arrays.asList("trade"); + private List ownedBlacklistSlots = Arrays.asList("mailbox", "trade"); // slots to which non-owners cannot equip private List ownerOnlySlots; diff --git a/src/games/stendhal/server/entity/player/PlayerRPClass.java b/src/games/stendhal/server/entity/player/PlayerRPClass.java index ca43f9b1fea..9680b1fb4b1 100644 --- a/src/games/stendhal/server/entity/player/PlayerRPClass.java +++ b/src/games/stendhal/server/entity/player/PlayerRPClass.java @@ -1,6 +1,6 @@ /* $Id$ */ /*************************************************************************** - * (C) Copyright 2003-2023 - Marauroa * + * (C) Copyright 2003-2024 - Marauroa * *************************************************************************** *************************************************************************** * * @@ -89,6 +89,9 @@ static void generateRPClass() { player.addRPSlot("bank_nalwor", 30, Definition.HIDDEN); player.addRPSlot("zaras_chest_ados", 30, Definition.HIDDEN); + // mail system + player.addRPSlot("mailbox", 1, Definition.HIDDEN); + // Kills recorder - needed for quest player.addRPSlot("!kills", 1, Definition.HIDDEN); diff --git a/src/games/stendhal/server/entity/slot/Mailbox.java b/src/games/stendhal/server/entity/slot/Mailbox.java new file mode 100644 index 00000000000..142f93136de --- /dev/null +++ b/src/games/stendhal/server/entity/slot/Mailbox.java @@ -0,0 +1,182 @@ +/*************************************************************************** + * Copyright © 2024 - Faiumoni e. V. * + *************************************************************************** + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +package games.stendhal.server.entity.slot; + +import java.sql.SQLException; + +import org.apache.log4j.Logger; + +import games.stendhal.server.entity.item.Item; +import games.stendhal.server.entity.item.OwnedItem; +import games.stendhal.server.entity.player.Player; +import marauroa.common.game.RPSlot; +import marauroa.common.game.SlotOwner; +import marauroa.server.db.DBTransaction; +import marauroa.server.db.TransactionPool; +import marauroa.server.game.db.CharacterDAO; +import marauroa.server.game.db.DAORegister; + + +/** + * Slot for managing item correspondence between players. + * + * FIXME: + * - might be better to use two class properties "inbox"/"outbox" + * - should not be able to send item to player if their inbox is already occupied + * - might be better to use slot(s) not attached to a specific player + */ +public class Mailbox extends PlayerSlot { + + private static final Logger logger = Logger.getLogger(Mailbox.class); + + public static enum MailboxResult { + CONTENTS_EMPTY, + CANNOT_CARRY, + INVALID_ITEM, + OWNED_ITEM, + RECEIVER_NOT_FOUND, + RECEIVER_ACCEPTED, + RECEIVER_DENIED, + ABORTER_ACCEPTED, + ABORTER_DENIED, + SQL_ERROR + } + + /** Name of player meant to receive contents. */ + private String target; + + + public Mailbox() { + super("mailbox"); + } + + /** + * Retrieves targeted receiver. + * + * @return + * Name of player to receive item. + */ + public String getTarget() { + return target; + } + + @Override + public int getCapacity() { + return 1; + } + + /** + * Configures player meant to received contents. + * + * @param target + * Player name. + * @param item + * Item to add to slot. + * @return + * Mailbox action result. + * @throws SQLException + */ + public MailboxResult requestSend(final String target, final Item item) { + if (item == null) { + return MailboxResult.INVALID_ITEM; + } + if (item.isBound() || (item instanceof OwnedItem && ((OwnedItem) item).hasOwner())) { + return MailboxResult.OWNED_ITEM; + } + final DBTransaction transaction = TransactionPool.get().beginWork(); + final CharacterDAO characterDAO = DAORegister.get().get(CharacterDAO.class); + String accountName; + try { + accountName = characterDAO.getAccountName(transaction, target); + if (accountName == null) { + return MailboxResult.RECEIVER_NOT_FOUND; + } + this.target = accountName; + // NOTE: does this also remove it from the source slot? + add(item); + return MailboxResult.RECEIVER_ACCEPTED; + } catch (final SQLException e) { + logger.error("Error occurred when looking up account for character \"" + target + "\"", e); + } + return MailboxResult.SQL_ERROR; + } + + /** + * Requests retrieval of contents. + * + * @param player + * Player making request. + * @return + * Mailbox action result. + */ + public MailboxResult requestReceive(final Player player) { + if (target == null) { + logger.warn("Target receiver not assigned for " + this); + return MailboxResult.RECEIVER_DENIED; + } + final String name = player.getName(); + if (!name.equals(target)) { + logger.warn("Requested contents of " + this + " for player " + name); + return MailboxResult.RECEIVER_DENIED; + } + final Item item = (Item) getFirst(); + if (item == null) { + logger.warn("Requested contents of empty " + this); + return MailboxResult.CONTENTS_EMPTY; + } + final RPSlot slot = player.getSlotToEquip(item); + if (slot == null) { + return MailboxResult.CANNOT_CARRY; + } + // NOTE: does this also remove it from the mailbox slot? + slot.add(item); + if (getFirst() != null) { + logger.warn("Contents received but contents not empty in " + this); + } + // reset receiver + target = null; + return MailboxResult.RECEIVER_ACCEPTED; + } + + /** + * Aborts request to send item and returns to sender. + * + * @param player + * Player requesting abort. + * @return + * Mailbox action result. + */ + public MailboxResult requestAbort(final Player player) { + final String name = player.getName(); + final SlotOwner owner = getOwner(); + if (!name.equals(owner.get("name"))) { + return MailboxResult.ABORTER_DENIED; + } + final Item item = (Item) getFirst(); + if (item == null) { + logger.warn("Requested contents of empty " + this); + return MailboxResult.CONTENTS_EMPTY; + } + final RPSlot slot = player.getSlotToEquip(item); + if (slot == null) { + return MailboxResult.CANNOT_CARRY; + } + // NOTE: does this also remove it from the mailbox slot? + slot.add(item); + if (getFirst() != null) { + logger.warn("Contents received but contents not empty in " + this); + } + // reset receiver + target = null; + return MailboxResult.ABORTER_ACCEPTED; + } +} From f2c2c7f1082e4fb8ad9100e74b376f81691b1b00 Mon Sep 17 00:00:00 2001 From: Jordan Irwin Date: Sun, 9 Jun 2024 11:19:39 -0700 Subject: [PATCH 3/4] Post office map --- data/conf/zones/semos.xml | 14 ++++ data/maps/Level 0/semos/plains_n.tmx | 6 +- data/maps/interiors/semos/post_office.tmx | 92 +++++++++++++++++++++++ 3 files changed, 109 insertions(+), 3 deletions(-) create mode 100644 data/maps/interiors/semos/post_office.tmx diff --git a/data/conf/zones/semos.xml b/data/conf/zones/semos.xml index 828e66a3db1..aa7ff8d5d38 100644 --- a/data/conf/zones/semos.xml +++ b/data/conf/zones/semos.xml @@ -1468,6 +1468,11 @@ Unfortunately I will not be able to visit Athor this year. I have run into a bit You find it hard to enter. Perhaps you're not prepared for what is below. + + + + + @@ -1551,6 +1556,15 @@ South: Semos city + + + + + + + + + diff --git a/data/maps/Level 0/semos/plains_n.tmx b/data/maps/Level 0/semos/plains_n.tmx index 69429357ebc..2c1a142a2a7 100644 --- a/data/maps/Level 0/semos/plains_n.tmx +++ b/data/maps/Level 0/semos/plains_n.tmx @@ -1,5 +1,5 @@ - + @@ -279,12 +279,12 @@ - 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 diff --git a/data/maps/interiors/semos/post_office.tmx b/data/maps/interiors/semos/post_office.tmx new file mode 100644 index 00000000000..390ae68f0a6 --- /dev/null +++ b/data/maps/interiors/semos/post_office.tmx @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + eJztzLkOQEAYReFR2bWWxtKiw7uhwJs7kilEYhDtX3zNzc2plFLJxYgJ88v9UCJDBO9kwYrt5R4i1S0H1g+2tKQlLWl9aAVwb9Ro0Bo+vm4ViA069Bgefjl2OZwP6Q== + + + + + eJxjYMAE/VB6Mpr4VCjdgUUPLtAExLOBeB6a+GsGhoZjROgXZMQuzgcVF8AiLwoUE8MiLoLDrFFAOiglUX0mDpyLRw6GcYHVRNq9EpjWcMnpAtOEHonpQhuoXgeLHkOgmBGJZukD1Rtg0WMKFDMj0SxjoHoTLHpGgh9lGXHHMT4gOwTKBADKYxR0 + + + + + eJxjYBgcQIiRgUGYcaBdMQpoDRYQqW4xA0MDDZ0xCkbBkAEAQpQB6w== + + + + + eJxjYBgFo2AUUANYMjIwWDGSpsccqN6CRD0DCajpRylGhgZy3CA1hMKLGgAACJ4Cgw== + + + + + eJxjYBgFo2AUjIJRQAwIGgA7ASrMAFM= + + + + + eJxTZ2BgUB/kGBtQR6PR5UgxixhALXNGqlm0SA/EuhFdz1AJr4E0B5d51DSLmn6ktln47KB3+QcAJagY/Q== + + + + + eJwTZ2RgEB/Fo3gUj+JRPCgxAMCsIdk= + + + From 0a6760d06ca84e8ae02bb58b12699c2ef875b2a5 Mon Sep 17 00:00:00 2001 From: Jordan Irwin Date: Sun, 9 Jun 2024 10:19:48 -0700 Subject: [PATCH 4/4] NPC for managing player mailbox (WIP) --- .../maps/semos/postoffice/PostmasterNPC.java | 265 ++++++++++++++++++ .../maps/semos/postoffice/package-info.java | 12 + 2 files changed, 277 insertions(+) create mode 100644 src/games/stendhal/server/maps/semos/postoffice/PostmasterNPC.java create mode 100644 src/games/stendhal/server/maps/semos/postoffice/package-info.java diff --git a/src/games/stendhal/server/maps/semos/postoffice/PostmasterNPC.java b/src/games/stendhal/server/maps/semos/postoffice/PostmasterNPC.java new file mode 100644 index 00000000000..90c01f275e0 --- /dev/null +++ b/src/games/stendhal/server/maps/semos/postoffice/PostmasterNPC.java @@ -0,0 +1,265 @@ +/*************************************************************************** + * Copyright © 2024 - Faiumoni e. V. * + *************************************************************************** + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +package games.stendhal.server.maps.semos.postoffice; + +import java.util.Map; + +import games.stendhal.common.Direction; +import games.stendhal.common.grammar.Grammar; +import games.stendhal.common.parser.Sentence; +import games.stendhal.server.core.config.ZoneConfigurator; +import games.stendhal.server.core.engine.StendhalRPZone; +import games.stendhal.server.entity.item.Item; +import games.stendhal.server.entity.npc.ChatAction; +import games.stendhal.server.entity.npc.ConversationPhrases; +import games.stendhal.server.entity.npc.ConversationStates; +import games.stendhal.server.entity.npc.EventRaiser; +import games.stendhal.server.entity.npc.SpeakerNPC; +import games.stendhal.server.entity.player.Player; +import games.stendhal.server.entity.slot.Mailbox; + + +/** + * NPC that manages postal transactions. + */ +public class PostmasterNPC implements ZoneConfigurator { + + /** Amount NPC will charge to send an item. */ + private static final int SERVICE_FEE = 100; + + + @Override + public void configureZone(final StendhalRPZone zone, final Map attributes) { + zone.add(configureNPC()); + } + + /** + * Configures NPC for managing player mailboxes. + * + * @return + * Postmaster NPC. + */ + private SpeakerNPC configureNPC() { + final SpeakerNPC npc = new SpeakerNPC("Postmaster Ellie"); + npc.setOutfit("body=1,head=0,mouth=2,eyes=24,dress=970,hair=50,mask=1,hat=990"); + npc.setPosition(9, 7); + npc.setIdleDirection(Direction.DOWN); + configurePostmaster(npc); + + // TODO: + // - create interior post office zone + // - create NPC dialogue + + return npc; + } + + /** + * Adds postmaster dialogue and logic to NPC. + * + * @param npc + * Postmaster NPC. + */ + private void configurePostmaster(final SpeakerNPC npc) { + npc.addGreeting("Hello. Welcome to Semos Post Office. How can I #help you?"); + npc.addGoodbye(); + npc.addHelp("I can help manage your #mailbox for a #fee."); + npc.addJob("I am postmaster of the Semos Post Office."); + npc.addQuest("Oh, no thank you. I don't need any help at the moment."); + npc.addOffer("The only thing I can offer is assistance with managing your #mailbox."); + + npc.add( + ConversationStates.ATTENDING, + "fee", + null, + ConversationStates.ATTENDING, + "The fee to #send an item is " + SERVICE_FEE + " money.", + null); + + npc.add( + ConversationStates.ATTENDING, + "mailbox", + null, + ConversationStates.ATTENDING, + "I can help you #send items to your friends. You can also check the current #status of your" + + " mailbox.", + null); + + npc.add( + ConversationStates.ATTENDING, + "status", + null, + ConversationStates.ATTENDING, + null, + new ChatAction() { + @Override + public void fire(Player player, Sentence sentence, EventRaiser raiser) { + reportStatus(player, npc); + } + }); + + npc.add( + ConversationStates.ATTENDING, + "send", + null, + ConversationStates.ATTENDING, + null, + new ChatAction() { + @Override + public void fire(Player player, Sentence sentence, EventRaiser raiser) { + // TODO: get target name & item from conversation + sendOutbox(player, npc, null, null); + } + }); + + final ChatAction receiveInboxAction = new ChatAction() { + @Override + public void fire(Player player, Sentence sentence, EventRaiser raiser) { + receiveInbox(player, npc); + } + }; + + npc.add( + ConversationStates.QUESTION_1, + ConversationPhrases.NO_MESSAGES, + null, + ConversationStates.ATTENDING, + "Okay, let me know how else I can #help you.", + null); + + npc.add( + ConversationStates.QUESTION_1, + ConversationPhrases.YES_MESSAGES, + null, + ConversationStates.ATTENDING, + null, + receiveInboxAction); + + npc.add( + ConversationStates.ATTENDING, + "receive", + null, + ConversationStates.ATTENDING, + null, + receiveInboxAction); + } + + /** + * Reports status of player's mailbox. + * + * @param player + * Player requesting status info. + * @param npc + * Postmaster NPC. + */ + private static void reportStatus(final Player player, final SpeakerNPC npc) { + String inbox = getInboxStatus(player); + String outbox = getOutboxStatus(player); + boolean offerReceive = true; + if (inbox == null && outbox == null) { + npc.say("I'm sorry, but I'm unable to check the status of your mailbox at the current time." + + " Please come back after we have worked out the problem."); + return; + } else if (inbox == null) { + offerReceive = false; + inbox = "I'm sorry, but I'm unable to check the status of your inbox at the current time." + + " Please come back after we have worked out the problem."; + } else if (outbox == null) { + outbox = "I'm sorry, but I'm unable to check the status of your outbox at the current time." + + " Please come back after we have worked out the problem."; + } + String msg = inbox + " " + outbox; + if (offerReceive) { + msg += " Would you like to get the item from your inbox?"; + npc.setCurrentState(ConversationStates.QUESTION_1); + } else { + msg += " Let me know if there is anything else I can #help you with."; + } + npc.say(msg); + } + + /** + * Gets message for player dependent on inbox status. + * + * @param player + * Player requesting status info. + * @return + * Inbox status message or {@code null}. + */ + private static String getInboxStatus(final Player player) { + // TODO: + return null; + } + + /** + * Gets a message for player dependent on outbox status. + * + * @param player + * Player requesting status info. + * @return + * Outbox status message or {@code null}. + */ + private static String getOutboxStatus(final Player player) { + final Mailbox mailbox = (Mailbox) player.getSlot("mailbox"); + if (mailbox == null) { + return null; + } + final Item item = (Item) mailbox.getFirst(); + if (item == null) { + return "Your outbox is empty."; + } else { + final String itemName = item.getName(); + final int count = item.getQuantity(); + final String target = mailbox.getTarget(); + if (target == null) { + return "Uh oh! You have " + Grammar.quantityNumberStrNoun(count, itemName) + + " ready to be mailed without anyone to receive it. Let me know if you would like to" + + "#cancel."; + } else { + return "You have " + Grammar.quantityNumberStrNoun(count, itemName) + + " waiting to be picked up by " + target + "."; + } + } + } + + /** + * Gives player item from inbox. + * + * @param player + * Player receiving item. + * @param npc + * Postmaster NPC. + */ + private static void receiveInbox(final Player player, final SpeakerNPC npc) { + // TODO: + npc.say("I'm sorry, the post office is still in its infancy and not fully functional. Please" + + " come back at another time to receive items."); + } + + /** + * Sends item to a player's inbox. + * + * @param player + * Player sending item. + * @param npc + * Postmaster NPC. + * @param target + * Name of player to receive item. + * @param item + * Item to be sent. + */ + private static void sendOutbox(final Player player, final SpeakerNPC npc, final String target, + final Item item) { + // TODO: + npc.say("I'm sorry, the post office is still in its infancy and not fully functional. Please" + + " come back at another time to send items."); + } +} diff --git a/src/games/stendhal/server/maps/semos/postoffice/package-info.java b/src/games/stendhal/server/maps/semos/postoffice/package-info.java new file mode 100644 index 00000000000..e9d8f447970 --- /dev/null +++ b/src/games/stendhal/server/maps/semos/postoffice/package-info.java @@ -0,0 +1,12 @@ +/*************************************************************************** + * Copyright © 2024 - Faiumoni e. V. * + *************************************************************************** + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +package games.stendhal.server.maps.semos.postoffice; \ No newline at end of file