diff --git a/src/games/stendhal/server/entity/npc/HealerNPC.java b/src/games/stendhal/server/entity/npc/HealerNPC.java new file mode 100644 index 00000000000..2d8821f3748 --- /dev/null +++ b/src/games/stendhal/server/entity/npc/HealerNPC.java @@ -0,0 +1,72 @@ +/*************************************************************************** + * 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.npc; + +import games.stendhal.server.entity.player.Player; +import games.stendhal.server.entity.status.PoisonStatus; + +/** + * NPC that services player HP. + */ +public class HealerNPC extends AttributeServicerNPC { + + /* The lowest possible price for a level 0 player. */ + private static Integer MIN_OFFER; + + + /** + * Creates a new healer NPC. + * + * @param name + * The NPC's name (please note that names should be unique). + */ + public HealerNPC(final String name) { + super(name, "hp"); + } + + @Override + public double fullService(final Player player) { + final double unitsServiced = super.fullService(player); + // healing also cures poison + player.getStatusList().removeAll(PoisonStatus.class); + return unitsServiced; + } + + /** + * Restores player's HP to full value and cures poison. + * + * @param player + * Player to be healed. + * @return + * Amount of HP restored. + */ + public int heal(final Player player) { + return (int) fullService(player); + } + + /** + * This is for registering a `games.stendhal.server.entity.npc.behaviour.impl.HealerBehaviour` + * with `games.stendhal.server.entity.npc.behaviour.journal.ServicersRegister`. + * + * @return + * The lowest possible price for a level 0 player. + */ + public int getMinOffer() { + if (HealerNPC.MIN_OFFER == null) { + // create a temporary level 0 player + final Player dummyPlayer = Player.createZeroLevelPlayer("_dummy_", null); + dummyPlayer.setHP(dummyPlayer.getHP()-1); + HealerNPC.MIN_OFFER = calculateOffer(dummyPlayer); + } + return HealerNPC.MIN_OFFER; + } +} diff --git a/src/games/stendhal/server/entity/npc/behaviour/adder/HealerAdder.java b/src/games/stendhal/server/entity/npc/behaviour/adder/HealerAdder.java index d55ce1e8d61..8635c3ec671 100644 --- a/src/games/stendhal/server/entity/npc/behaviour/adder/HealerAdder.java +++ b/src/games/stendhal/server/entity/npc/behaviour/adder/HealerAdder.java @@ -12,6 +12,8 @@ ***************************************************************************/ package games.stendhal.server.entity.npc.behaviour.adder; +import org.apache.log4j.Logger; + import games.stendhal.common.constants.SoundID; import games.stendhal.common.constants.SoundLayer; import games.stendhal.common.grammar.ItemParserResult; @@ -21,6 +23,7 @@ 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.HealerNPC; import games.stendhal.server.entity.npc.SpeakerNPC; import games.stendhal.server.entity.npc.behaviour.impl.HealerBehaviour; import games.stendhal.server.entity.npc.behaviour.journal.ServicersRegister; @@ -59,14 +62,105 @@ public class HealerAdder { */ public void addHealer(final SpeakerNPC npc, final int cost) { final HealerBehaviour healerBehaviour = new HealerBehaviour(cost); + // FIXME: should registration be moved to main `addHealer` method servicersRegister.add(npc.getName(), healerBehaviour); + addHealer(npc, createCalculateCostAction(healerBehaviour), createHealAction(healerBehaviour)); + } + + /** + * Makes the NPC a healer. + * + * @param npc + * SpeakerNPC who does the healing. + * @param calculateCostAction + * Action to take to determine cost. + * @param healAction + * Action to take when player is healed. + */ + public void addHealer(final SpeakerNPC npc, final ChatAction calculateCostAction, final ChatAction healAction) { + // Give attribute to healers + npc.put("job_healer", ""); + + final Engine engine = npc.getEngine(); + + engine.add(ConversationStates.ATTENDING, + ConversationPhrases.OFFER_MESSAGES, + null, + false, + ConversationStates.ATTENDING, + "I can #heal you.", + null); + + engine.add(ConversationStates.ATTENDING, + "heal", + null, + false, + ConversationStates.ATTENDING, + null, + calculateCostAction); + + engine.add(ConversationStates.HEAL_OFFERED, + ConversationPhrases.YES_MESSAGES, + null, + false, + ConversationStates.ATTENDING, + null, + healAction); - final ChatAction calculateCostAction = new ChatAction() { + engine.add(ConversationStates.HEAL_OFFERED, + ConversationPhrases.NO_MESSAGES, + null, + false, + ConversationStates.ATTENDING, + "OK, how else may I help you?", + null); + } + + /** + * Makes the NPC a healer. + * + * @param npc + * NPC who does the healing. + */ + public void addHealer(final HealerNPC npc) { + // FIXME: + // - should registration be moved to main `addHealer` method + // - registration doesn't support dynamic pricing of `HealerNPC` class. + servicersRegister.add(npc.getName(), new HealerBehaviour(npc.getMinOffer())); + addHealer(npc, createCalculateCostAction(npc), createHealAction(npc)); + } + + /** + * Creates action to calculate cost. + * + * @param obj + * `HealerBehaviour` or `HealerNPC` instance. + * @return + * Action executed when player asks to be healed. + */ + private ChatAction createCalculateCostAction(final Object obj) { + final HealerBehaviour healerBehaviour = obj instanceof HealerBehaviour ? (HealerBehaviour) obj : null; + final HealerNPC healer = obj instanceof HealerNPC ? (HealerNPC) obj : null; + final boolean usingBehaviour = healerBehaviour != null; + + if (!usingBehaviour && healer == null) { + Logger.getLogger(HealerAdder.class).error("Parameter type must be of `" + HealerBehaviour.class.getName() + + "` or `" + HealerNPC.class.getName() + "`", new Throwable()); + return null; + } + + return new ChatAction() { + @SuppressWarnings("null") @Override public void fire(final Player player, final Sentence sentence, final EventRaiser raiser) { + int cost = 0; ItemParserResult res = new ItemParserResult(true, "heal", 1, null); - - int cost = healerBehaviour.getCharge(res, player); + if (usingBehaviour) { + cost = healerBehaviour.getCharge(res, player); + } else { + healer.setOffer(healer.calculateOffer(player)); + cost = healer.getOffer(); + } currentBehavRes = res; String badboymsg = ""; @@ -92,6 +186,10 @@ public void fire(final Player player, final Sentence sentence, final EventRaiser raiser.setCurrentState(ConversationStates.HEAL_OFFERED); // success } else { + if (!usingBehaviour) { + raiser.say("You do not need to be healed."); + return; + } if ((player.getAtk() > 35) || (player.getDef() > 35)) { raiser.say("Sorry, I cannot heal you because you are way too strong for my limited powers."); } else if ((!player.isNew() @@ -104,82 +202,70 @@ public void fire(final Player player, final Sentence sentence, final EventRaiser // (low atk, low def AND low level) raiser.say("Sorry, but you have a bad aura, so that I am unable to heal you right now."); } else { - healerBehaviour.heal(player); + if (usingBehaviour) { + healerBehaviour.heal(player); + } else { + healer.heal(player); + } raiser.addEvent(new SoundEvent(SoundID.HEAL, SoundLayer.CREATURE_NOISE)); raiser.say("There, you are healed. How else may I help you?"); } } } }; + } - final ChatAction healAction = new ChatAction() { + /** + * Creates action to heal player. + * + * @param obj + * `HealerBehaviour` or `HealerNPC` instance. + * @return + * Action executed when player confirms to be healed. + */ + private ChatAction createHealAction(final Object obj) { + final HealerBehaviour healerBehaviour = obj instanceof HealerBehaviour ? (HealerBehaviour) obj : null; + final HealerNPC healer = obj instanceof HealerNPC ? (HealerNPC) obj : null; + final boolean usingBehaviour = healerBehaviour != null; + + if (!usingBehaviour && healer == null) { + Logger.getLogger(HealerAdder.class).error("Parameter type must be of `" + HealerBehaviour.class.getName() + + "` or `" + HealerNPC.class.getName() + "`", new Throwable()); + return null; + } + + return new ChatAction() { + @SuppressWarnings("null") @Override public void fire(final Player player, final Sentence sentence, final EventRaiser raiser) { - int cost = healerBehaviour.getCharge(currentBehavRes, player); + int cost = 0; + if (usingBehaviour) { + cost = healerBehaviour.getCharge(currentBehavRes, player); + } else { + cost = healer.calculateOffer(player); + if (healer.offerChanged(cost)) { + raiser.say("I cannot heal you while you are actively fighting. Ask me again later."); + raiser.setCurrentState(ConversationStates.ATTENDING); + healer.resetOffer(); + return; + } + } if (cost < 0) { cost = player.getLevel() * Math.abs(cost) + 1; } if (player.drop("money", cost)) { - healerBehaviour.heal(player); + if (usingBehaviour) { + healerBehaviour.heal(player); + } else { + healer.heal(player); + } raiser.addEvent(new SoundEvent(SoundID.HEAL, SoundLayer.CREATURE_NOISE)); raiser.say("There, you are healed. How else may I help you?"); } else { raiser.say("I'm sorry, but it looks like you can't afford it."); } - currentBehavRes = null; } }; - - addHealer(npc, calculateCostAction, healAction); - } - - /** - * Makes the NPC a healer. - * - * @param npc - * SpeakerNPC who does the healing. - * @param calculateCostAction - * Action to take to determine cost. - * @param healAction - * Action to take when player is healed. - */ - public void addHealer(final SpeakerNPC npc, final ChatAction calculateCostAction, final ChatAction healAction) { - // Give attribute to healers - npc.put("job_healer", ""); - - final Engine engine = npc.getEngine(); - - engine.add(ConversationStates.ATTENDING, - ConversationPhrases.OFFER_MESSAGES, - null, - false, - ConversationStates.ATTENDING, - "I can #heal you.", - null); - - engine.add(ConversationStates.ATTENDING, - "heal", - null, - false, - ConversationStates.ATTENDING, - null, - calculateCostAction); - - engine.add(ConversationStates.HEAL_OFFERED, - ConversationPhrases.YES_MESSAGES, - null, - false, - ConversationStates.ATTENDING, - null, - healAction); - - engine.add(ConversationStates.HEAL_OFFERED, - ConversationPhrases.NO_MESSAGES, - null, - false, - ConversationStates.ATTENDING, - "OK, how else may I help you?", - null); } }