From d53cb31ccce21e4101e0765d7e484d9ddf461169 Mon Sep 17 00:00:00 2001 From: Jordan Irwin Date: Fri, 19 Apr 2024 23:15:00 -0700 Subject: [PATCH] Punishment for visiting Kika clouds too frequently --- .../entity/item/scroll/BalloonScroll.java | 10 +- .../kikareukin/islands/HeavenGateKeeper.java | 175 ++++++++++++++++++ 2 files changed, 184 insertions(+), 1 deletion(-) create mode 100644 src/games/stendhal/server/maps/kikareukin/islands/HeavenGateKeeper.java diff --git a/src/games/stendhal/server/entity/item/scroll/BalloonScroll.java b/src/games/stendhal/server/entity/item/scroll/BalloonScroll.java index 334dbe054ce..782271b5d42 100644 --- a/src/games/stendhal/server/entity/item/scroll/BalloonScroll.java +++ b/src/games/stendhal/server/entity/item/scroll/BalloonScroll.java @@ -1,6 +1,6 @@ /* $Id$ */ /*************************************************************************** - * (C) Copyright 2003-2010 - Stendhal * + * (C) Copyright 2003-2024 - Stendhal * *************************************************************************** *************************************************************************** * * @@ -17,6 +17,7 @@ import games.stendhal.common.MathHelper; import games.stendhal.server.core.events.DelayedPlayerTextSender; import games.stendhal.server.entity.player.Player; +import games.stendhal.server.maps.kikareukin.islands.HeavenGateKeeper; /** * Represents the balloon that takes the player to 7 kikareukin clouds, @@ -73,6 +74,13 @@ protected boolean useTeleportScroll(final Player player) { } return false; } + + if (!HeavenGateKeeper.requestEntrance(player)) { + // balloon is used if player was punished + removeOne(); + return false; + } + long lastuse = -1; if (player.hasQuest("balloon")) { lastuse = Long.parseLong(player.getQuest("balloon")); diff --git a/src/games/stendhal/server/maps/kikareukin/islands/HeavenGateKeeper.java b/src/games/stendhal/server/maps/kikareukin/islands/HeavenGateKeeper.java new file mode 100644 index 00000000000..bf4672bdde8 --- /dev/null +++ b/src/games/stendhal/server/maps/kikareukin/islands/HeavenGateKeeper.java @@ -0,0 +1,175 @@ +/*************************************************************************** + * 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.kikareukin.islands; + +import games.stendhal.common.Level; +import games.stendhal.common.MathHelper; +import games.stendhal.common.NotificationType; +import games.stendhal.server.core.engine.SingletonRepository; +import games.stendhal.server.core.engine.StendhalRPZone; +import games.stendhal.server.core.events.TurnListener; +import games.stendhal.server.core.events.TurnNotifier; +import games.stendhal.server.entity.npc.action.IncrementQuestAction; +import games.stendhal.server.entity.player.Player; + + +public class HeavenGateKeeper { + + /** Slot name used for tracking requests. */ + private static final String SLOT = "heaven_gatekeeper"; + /** Name of entity that messages player. */ + private static final String ENTITY_NAME = "Kikareukin's Gatekeeper"; + /** Number of requests allowed before being punished. */ + private static final short REQUEST_LIMIT = 3; + /** Time period in which player requests are tracked (4 days). */ + private static final long TIME_BUFFER = MathHelper.MILLISECONDS_IN_ONE_DAY * 4; + /** XP modifier for trying to requesting too often (15% of excess). */ + private static final float XP_MODIFIER = 0.15f; + + + /** + * Increments number of requests player has made. + * + * @param player + * Player requesting entrance. + */ + private static void addRequest(final Player player) { + final long timeFromRequestStart = System.currentTimeMillis() - HeavenGateKeeper.getRequestTime(player); + if (timeFromRequestStart > HeavenGateKeeper.TIME_BUFFER) { + // first request or time limit expired so reset so player isn't unnecessarily punished + player.setQuest(HeavenGateKeeper.SLOT, System.currentTimeMillis() + ";1"); + } else { + new IncrementQuestAction(HeavenGateKeeper.SLOT, 1, 1).fire(player, null, null); + } + } + + /** + * Retrieves the time period beginning when player made initial request. + * + * @param player + * Player requesting entrance. + * @return + * Time of initial request. + */ + private static long getRequestTime(final Player player) { + return MathHelper.parseLongDefault(player.getQuest(HeavenGateKeeper.SLOT, 0), 0); + } + + /** + * Retrieves number of request player has made in the current time period. + * + * @param player + * Player requesting entrance. + * @return + * Request count. + */ + private static int getRequestCount(final Player player) { + return MathHelper.parseIntDefault(player.getQuest(HeavenGateKeeper.SLOT, 1), 0); + } + + /** + * Punishment from heaven. + * + * - sent to afterlife + * - loses all but 1 HP + * - loses 15% of excess XP + * + * TODO: + * - add lightning (spell effect) + * - play thunder sound + * + * @param player + * Player being punished. + */ + private static void punish(final Player player) { + // send to afterlife + final StendhalRPZone afterlife = SingletonRepository.getRPWorld().getZone("int_afterlife"); + if (afterlife != null) { + player.teleport(afterlife, 31, 23, player.getDirection(), null); + } + // set HP to 1 & subtract XP + final int xpStart = player.getXP(); + // a percentage of XP based on difference requirement to next level (levels can be lost) + //final int xpDiff = (int) Math.floor(Level.getXPDiff(player.getLevel()-1) * HeavenGateKeeper.XP_MODIFIER); + final int xpBuffer = Math.max(xpStart - Level.getXP(player.getLevel()), 0); + // a percentage of player's gained XP relative to the current level (levels cannot be lost) + final int xpDiff = (int) Math.floor(xpBuffer * HeavenGateKeeper.XP_MODIFIER); + final int hpStart = player.getHP(); + player.addXP(-xpDiff); + player.setHP(1); + final int xpLoss = xpStart - player.getXP(); + final int hpLoss = hpStart - player.getHP(); + + NotificationType ntype = NotificationType.INFORMATION; + String msg = "It appears you you are not welcome in the clouds at this time."; + if (xpLoss > 0 || hpLoss > 0) { + ntype = NotificationType.NEGATIVE; + msg += " You lost "; + if (hpLoss > 0) { + msg += hpLoss + " health"; + if (xpLoss > 0) { + msg += " and "; + } + } + if (xpLoss > 0) { + msg += xpLoss + " experience"; + } + msg += "."; + } + player.sendPrivateText(ntype, msg); + } + + /** + * Checks if player violates visit limit. + * + * @param player + * Player requesting entrance. + * @return + * `true` if player has made more than 3 requests within 4 days. + */ + private static boolean inViolation(final Player player) { + return HeavenGateKeeper.getRequestCount(player) > HeavenGateKeeper.REQUEST_LIMIT; + } + + /** + * Requests entrance into heaven and punishes if necessary. + * + * @param player + * Player requesting entrance. + * @return + * Whether player can enter. + */ + public static boolean requestEntrance(final Player player) { + HeavenGateKeeper.addRequest(player); + if (HeavenGateKeeper.inViolation(player)) { + // first delay is to notify player, second is to apply punishment + final TurnNotifier notifier = SingletonRepository.getTurnNotifier(); + final TurnListener listener = new TurnListener() { + private boolean notified = false; + @Override + public void onTurnReached(int currentTurn) { + if (!notified) { + player.sendPrivateText(NotificationType.PRIVMSG, HeavenGateKeeper.ENTITY_NAME, + "You have worn out your welcome and shall be punished for your greed!"); + notified = true; + notifier.notifyInTurns(10, this); + } else { + HeavenGateKeeper.punish(player); + } + } + }; + notifier.notifyInTurns(10, listener); + return false; + } + return true; + } +}