mirror of
https://gitlab.com/2009scape/2009scape.git
synced 2025-12-21 09:02:07 -07:00
Graves Rewrite
Rewrote graves from scratch using new systems and abundant unit testing Fixed a race condition bug that would prevent classes implementing PersistWorld from properly... persisting Fixed a bug where graves were (still) too generous Fixed a bug where graves would not persist across server reboots Fixed a bug where graves would not recognize a relogged player Fixed numerous other bugs with graves, see the unit tests for complete coverage Grave system now supports quest requirements
This commit is contained in:
parent
95bd5ab4b7
commit
f406b8aa98
23 changed files with 948 additions and 797 deletions
|
|
@ -6,6 +6,8 @@ import core.game.node.entity.player.info.login.PlayerParser;
|
|||
import core.game.node.entity.player.link.audio.Audio;
|
||||
import core.game.node.item.GroundItemManager;
|
||||
import core.game.node.item.Item;
|
||||
import rs09.game.node.entity.player.graves.Grave;
|
||||
import rs09.game.node.entity.player.graves.GraveController;
|
||||
import rs09.game.system.SystemLogger;
|
||||
import rs09.game.system.config.ItemConfigParser;
|
||||
import rs09.game.world.GameWorld;
|
||||
|
|
@ -42,6 +44,10 @@ public final class DropItemHandler {
|
|||
player.getDialogueInterpreter().open(9878, item);
|
||||
return true;
|
||||
}
|
||||
if (GraveController.hasGraveAt(player.getLocation())) {
|
||||
player.sendMessage("You cannot drop items on top of graves!");
|
||||
return false;
|
||||
}
|
||||
if (player.getAttribute("equipLock:" + item.getId(), 0) > GameWorld.getTicks()) {
|
||||
SystemLogger.logAlert(player + ", tried to do the drop & equip dupe.");
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -237,8 +237,6 @@ public final class FatherAereckDialogue extends DialoguePlugin {
|
|||
case 10:
|
||||
end();
|
||||
player.getInterfaceManager().open(new Component(652));
|
||||
BitregisterAssembler.send(player, 652, 34, 0, 13, new BitregisterAssembler(0));
|
||||
player.getConfigManager().set(1146, player.getGraveManager().getType().ordinal() | 262112);
|
||||
player.getAchievementDiaryManager().finishTask(player, DiaryType.LUMBRIDGE, 0, 15);
|
||||
break;
|
||||
case 20:
|
||||
|
|
|
|||
|
|
@ -1,44 +0,0 @@
|
|||
package core.game.interaction.inter;
|
||||
|
||||
import core.game.component.Component;
|
||||
import core.game.component.ComponentDefinition;
|
||||
import core.game.component.ComponentPlugin;
|
||||
import core.game.node.entity.player.Player;
|
||||
import core.game.node.entity.player.link.grave.GraveType;
|
||||
import core.game.node.item.Item;
|
||||
import core.plugin.Initializable;
|
||||
import core.plugin.Plugin;
|
||||
|
||||
/**
|
||||
* Represents the component plugin used for the grave purchasing interface.
|
||||
* @author Vexia
|
||||
*/
|
||||
@Initializable
|
||||
public final class GravePurchaseInterface extends ComponentPlugin {
|
||||
|
||||
@Override
|
||||
public Plugin<Object> newInstance(Object arg) throws Throwable {
|
||||
ComponentDefinition.put(652, this);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handle(Player player, Component component, int opcode, int button, int slot, int itemId) {
|
||||
if (slot == -1) {
|
||||
return true;
|
||||
}
|
||||
final GraveType grave = GraveType.values()[slot];
|
||||
int cost = grave.getCost();
|
||||
if (!player.getInventory().containsItem(new Item(995, cost))) {
|
||||
return true;
|
||||
}
|
||||
if (!player.getInventory().remove(new Item(995, cost)) && grave != GraveType.MEMORIAL_PLAQUE) {
|
||||
player.getPacketDispatch().sendMessage("You don't have enough coins to buy this grave stone.");
|
||||
return true;
|
||||
}
|
||||
player.getGraveManager().setType(grave);
|
||||
player.getDialogueInterpreter().sendDialogue("Your gravestone has been changed as you requested.");
|
||||
player.getInterfaceManager().close();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,470 +0,0 @@
|
|||
package core.game.node.entity.npc.other;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import core.cache.def.impl.NPCDefinition;
|
||||
import core.game.component.Component;
|
||||
import core.game.node.entity.skill.Skills;
|
||||
import core.game.interaction.OptionHandler;
|
||||
import core.game.node.Node;
|
||||
import core.game.node.entity.Entity;
|
||||
import core.game.node.entity.combat.CombatStyle;
|
||||
import core.game.node.entity.npc.AbstractNPC;
|
||||
import core.game.node.entity.player.Player;
|
||||
import core.game.node.entity.player.link.HintIconManager;
|
||||
import core.game.node.entity.player.link.grave.GraveManager;
|
||||
import core.game.node.entity.player.link.grave.GraveType;
|
||||
import core.game.node.item.GroundItem;
|
||||
import rs09.game.world.GameWorld;
|
||||
import core.game.world.map.Location;
|
||||
import rs09.game.world.repository.Repository;
|
||||
import core.game.world.update.flag.context.Animation;
|
||||
import core.game.world.update.flag.context.Graphics;
|
||||
import core.plugin.Plugin;
|
||||
import core.plugin.Initializable;
|
||||
import rs09.plugin.ClassScanner;
|
||||
|
||||
/**
|
||||
* Handles a gravestone npc.
|
||||
* @author Vexia
|
||||
*/
|
||||
@Initializable
|
||||
public class GraveStoneNPC extends AbstractNPC {
|
||||
|
||||
/**
|
||||
* The owner of the gravestone.
|
||||
*/
|
||||
private String owner;
|
||||
|
||||
/**
|
||||
* The display name.
|
||||
*/
|
||||
private String display;
|
||||
|
||||
/**
|
||||
* The gravestone type.
|
||||
*/
|
||||
private GraveType type;
|
||||
|
||||
/**
|
||||
* The life decay time.
|
||||
*/
|
||||
private int life = -1;
|
||||
|
||||
/**
|
||||
* If the stone is blessed.
|
||||
*/
|
||||
private boolean blessed;
|
||||
|
||||
/**
|
||||
* The ground items list.
|
||||
*/
|
||||
private List<GroundItem> items;
|
||||
|
||||
/**
|
||||
* Constructs a new {@Code GraveStoneNPC} {@Code Object}
|
||||
*/
|
||||
public GraveStoneNPC() {
|
||||
super(-1, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new {@Code GraveStoneNPC} {@Code Object}
|
||||
* @param id the id.
|
||||
* @param location the location.
|
||||
*/
|
||||
public GraveStoneNPC(int id, Location location) {
|
||||
super(id, location);
|
||||
super.setWalks(false);
|
||||
super.setNeverWalks(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
super.init();
|
||||
Player player = Repository.getPlayerByName(owner);
|
||||
if (player != null) {
|
||||
HintIconManager.registerHintIcon(player, this);
|
||||
}
|
||||
lock();
|
||||
animate(Animation.create(7394));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleTickActions() {
|
||||
if (life == -1) {
|
||||
return;
|
||||
}
|
||||
if (life < GameWorld.getTicks()) {
|
||||
clear();
|
||||
message("Your gravestone has collapsed.");
|
||||
return;
|
||||
}
|
||||
int minutes = getMinutes();
|
||||
if (minutes <= 1) {
|
||||
int seconds = getSeconds();
|
||||
int transformId = -1;
|
||||
if (seconds == 100) {
|
||||
transformId = getOriginalId() + 1;
|
||||
} else if (seconds == 30) {
|
||||
transformId = getOriginalId() + 2;
|
||||
}
|
||||
if (transformId != -1) {
|
||||
transform(transformId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
super.clear();
|
||||
Player player = Repository.getPlayerByName(owner);
|
||||
if (player != null) {
|
||||
player.getHintIconManager().clear();
|
||||
}
|
||||
if (owner != null) {
|
||||
GraveManager.getGraves().remove(owner);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAttackable(Entity entity, CombatStyle style, boolean message) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canAttack(final Entity victim) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object fireEvent(String identifier, Object... objects) {
|
||||
switch (identifier) {
|
||||
case "updateItems":
|
||||
for (GroundItem item : items) {
|
||||
if (item == null) {
|
||||
continue;
|
||||
}
|
||||
item.setDropper((Player) objects[0]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public AbstractNPC construct(int id, Location location, Object... objects) {
|
||||
GraveStoneNPC npc = new GraveStoneNPC(id, location);
|
||||
if (objects != null && objects.length > 1) {
|
||||
npc.setOwner((String) objects[0]);
|
||||
npc.setLife((int) objects[1]);
|
||||
npc.setItems((List<GroundItem>) objects[2]);
|
||||
npc.setType((GraveType) objects[3]);
|
||||
npc.setDisplay((String) objects[4]);
|
||||
}
|
||||
GraveManager.getGraves().put(npc.getOwner(), npc);
|
||||
return npc;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Plugin<Object> newInstance(Object arg) throws Throwable {
|
||||
ClassScanner.definePlugin(new GraveStonePlugin());
|
||||
return super.newInstance(arg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] getIds() {
|
||||
return new int[] { 6565, 6568, 6571, 6574, 6577, 6580, 6583, 6586, 6589, 6592, 6595, 6598, 6601 };
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the grave message.
|
||||
* @param player the player.
|
||||
*/
|
||||
public void read(Player player) {
|
||||
player.getConfigManager().set(1146, 1);
|
||||
player.getInterfaceManager().open(new Component(266));
|
||||
if (!isOwner(player.getName())) {
|
||||
player.getPacketDispatch().sendString(getMessage(), 266, 23);
|
||||
} else {
|
||||
final int minutes = getMinutes();
|
||||
String message;
|
||||
if (minutes < 1) {
|
||||
int seconds = getSeconds();
|
||||
message = "It looks like it'll survive another " + (seconds > 1 ? seconds + " seconds." : "second.");
|
||||
} else {
|
||||
message = "It looks like it'll survive another " + (minutes > 1 ? minutes + " minutes" : "minute") + ".";
|
||||
}
|
||||
player.getPacketDispatch().sendMessage(message);
|
||||
player.getPacketDispatch().sendString(getMessage(), 266, 23);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Repairs the grave.
|
||||
* @param player the player.
|
||||
*/
|
||||
public void repair(Player player) {
|
||||
if (player.getSkills().getStaticLevel(Skills.PRAYER) < 2) {
|
||||
player.getDialogueInterpreter().sendDialogue("You need a prayer level of 2 to repair a gravestone.");
|
||||
return;
|
||||
}
|
||||
if (getId() == getOriginalId()) {
|
||||
player.sendMessage("This grave does not need repairing.");
|
||||
return;
|
||||
}
|
||||
int seconds = type.getDecay() * 60;
|
||||
int ticks = (1000 * seconds) / 600;
|
||||
reTransform();
|
||||
updateItems(ticks);
|
||||
setLife(GameWorld.getTicks() + ticks);
|
||||
}
|
||||
|
||||
/**
|
||||
* Blesses the grave.
|
||||
* @param player the player.
|
||||
*/
|
||||
public void bless(Player player) {
|
||||
if (isOwner(player.getName())) {
|
||||
player.getPacketDispatch().sendMessage("The gods don't seem to approve of people attempting to bless their own");
|
||||
player.getPacketDispatch().sendMessage("gravestones.");
|
||||
return;
|
||||
}
|
||||
if (player.getSkills().getStaticLevel(Skills.PRAYER) < 70) {
|
||||
player.getDialogueInterpreter().sendDialogue("You need a prayer level of 70 to bless a gravestone.");
|
||||
return;
|
||||
}
|
||||
if (player.getSkills().getPrayerPoints() == 0) {
|
||||
player.getDialogueInterpreter().sendDialogue("You don't have enough prayer points to do that.");
|
||||
return;
|
||||
}
|
||||
if (isBlessed()) {
|
||||
player.getPacketDispatch().sendMessage("This gravestone has already been blessed.");
|
||||
return;
|
||||
}
|
||||
reTransform();
|
||||
setBlessed(true);
|
||||
updateItems(6100);
|
||||
player.animate(Animation.create(645));
|
||||
graphics(Graphics.create(1274));
|
||||
setLife(GameWorld.getTicks() + 6000);
|
||||
message(player.getUsername() + " has blessed your grave, it will remain for another 60 minutes.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Demolishes the grave.
|
||||
* @param player the player.
|
||||
*/
|
||||
public void demolish(Player player) {
|
||||
if (!isOwner(player.getName())) {
|
||||
player.getPacketDispatch().sendMessage("This is not your grave!");
|
||||
return;
|
||||
}
|
||||
clear();
|
||||
player.sendMessage("You destroyed your grave!");
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the items with a new decay.
|
||||
* @param ticks the ticks.
|
||||
*/
|
||||
private void updateItems(int ticks) {
|
||||
if (getItems() != null) {
|
||||
for (GroundItem item : getItems()) {
|
||||
if (item == null) {
|
||||
continue;
|
||||
}
|
||||
if (item.isActive()) {
|
||||
item.setDecayTime(ticks);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Messages the owner.
|
||||
* @param message the message.
|
||||
*/
|
||||
private void message(String message) {
|
||||
Player o = Repository.getPlayerByName(owner);
|
||||
if (o != null && o.isActive()) {
|
||||
o.sendMessage(message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the minutes left.
|
||||
* @return the minutes.
|
||||
*/
|
||||
public int getMinutes() {
|
||||
return (life - GameWorld.getTicks()) / 100;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the seconds left.
|
||||
* @return the seconds.
|
||||
*/
|
||||
public int getSeconds() {
|
||||
return (life - GameWorld.getTicks()) * 600 / 1000;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the message.
|
||||
* @return the message.
|
||||
*/
|
||||
public String getMessage() {
|
||||
int mins = getMinutes();
|
||||
return type.getMessage().replace("@name", display).replace("@mins", (mins > 1 ? mins + " minutes" : mins + " minute"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this name is the owner.
|
||||
* @param name the name.
|
||||
* @return {@code True} if so.
|
||||
*/
|
||||
public boolean isOwner(String name) {
|
||||
return owner.equals(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the owner.
|
||||
* @return the owner
|
||||
*/
|
||||
public String getOwner() {
|
||||
return owner;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the display.
|
||||
* @return the display
|
||||
*/
|
||||
public String getDisplay() {
|
||||
return display;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the badisplay.
|
||||
* @param display the display to set.
|
||||
*/
|
||||
public void setDisplay(String display) {
|
||||
this.display = display;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the baowner.
|
||||
* @param owner the owner to set.
|
||||
*/
|
||||
public void setOwner(String owner) {
|
||||
this.owner = owner;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the life.
|
||||
* @return the life
|
||||
*/
|
||||
public int getLife() {
|
||||
return life;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the balife.
|
||||
* @param life the life to set.
|
||||
*/
|
||||
public void setLife(int life) {
|
||||
this.life = life;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the type.
|
||||
* @return the type
|
||||
*/
|
||||
public GraveType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the batype.
|
||||
* @param type the type to set.
|
||||
*/
|
||||
public void setType(GraveType type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the blessed.
|
||||
* @return the blessed
|
||||
*/
|
||||
public boolean isBlessed() {
|
||||
return blessed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the bablessed.
|
||||
* @param blessed the blessed to set.
|
||||
*/
|
||||
public void setBlessed(boolean blessed) {
|
||||
this.blessed = blessed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the items.
|
||||
* @return the items
|
||||
*/
|
||||
public List<GroundItem> getItems() {
|
||||
return items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the baitems.
|
||||
* @param items the items to set.
|
||||
*/
|
||||
public void setItems(List<GroundItem> items) {
|
||||
this.items = items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the interactions of a grave stone.
|
||||
* @author Vexia
|
||||
*/
|
||||
public class GraveStonePlugin extends OptionHandler {
|
||||
|
||||
@Override
|
||||
public Plugin<Object> newInstance(Object arg) throws Throwable {
|
||||
for (GraveType g : GraveType.values()) {
|
||||
for (int start = g.getNpcId(); start < g.getNpcId() + 3; start++) {
|
||||
NPCDefinition.forId(start).getHandlers().put("option:read", this);
|
||||
NPCDefinition.forId(start).getHandlers().put("option:bless", this);
|
||||
NPCDefinition.forId(start).getHandlers().put("option:demolish", this);
|
||||
NPCDefinition.forId(start).getHandlers().put("option:repair", this);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handle(Player player, Node node, String option) {
|
||||
if (!(node instanceof GraveStoneNPC)) {
|
||||
player.sendMessage("Error! NPC is not instanceof a GraveStoneNPC.");
|
||||
return true;
|
||||
}
|
||||
GraveStoneNPC grave = (GraveStoneNPC) node;
|
||||
switch (option) {
|
||||
case "read":
|
||||
grave.read(player);
|
||||
break;
|
||||
case "bless":
|
||||
grave.bless(player);
|
||||
break;
|
||||
case "demolish":
|
||||
grave.demolish(player);
|
||||
break;
|
||||
case "repair":
|
||||
grave.repair(player);
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -25,7 +25,6 @@ import core.game.node.entity.player.link.appearance.Appearance;
|
|||
import core.game.node.entity.player.link.audio.AudioManager;
|
||||
import core.game.node.entity.player.link.diary.AchievementDiaryManager;
|
||||
import core.game.node.entity.player.link.emote.EmoteManager;
|
||||
import core.game.node.entity.player.link.grave.GraveManager;
|
||||
import core.game.node.entity.player.link.music.MusicPlayer;
|
||||
import core.game.node.entity.player.link.prayer.Prayer;
|
||||
import core.game.node.entity.player.link.prayer.PrayerType;
|
||||
|
|
@ -35,7 +34,6 @@ import core.game.node.entity.player.link.skillertasks.SkillerTasks;
|
|||
import core.game.node.entity.skill.Skills;
|
||||
import core.game.node.entity.skill.construction.HouseManager;
|
||||
import core.game.node.entity.skill.summoning.familiar.FamiliarManager;
|
||||
import core.game.node.item.GroundItem;
|
||||
import core.game.node.item.GroundItemManager;
|
||||
import core.game.node.item.Item;
|
||||
import core.game.system.communication.CommunicationInfo;
|
||||
|
|
@ -65,13 +63,15 @@ import kotlin.Unit;
|
|||
import kotlin.jvm.functions.Function1;
|
||||
import org.rs09.consts.Items;
|
||||
import proto.management.ClanLeaveNotification;
|
||||
import proto.management.LeaveClanRequest;
|
||||
import proto.management.PlayerStatusUpdate;
|
||||
import rs09.GlobalStats;
|
||||
import rs09.ServerConstants;
|
||||
import rs09.game.VarpManager;
|
||||
import rs09.game.node.entity.combat.CombatSwingHandler;
|
||||
import rs09.game.node.entity.combat.equipment.EquipmentDegrader;
|
||||
import rs09.game.node.entity.player.graves.Grave;
|
||||
import rs09.game.node.entity.player.graves.GraveType;
|
||||
import rs09.game.node.entity.player.graves.GraveController;
|
||||
import rs09.game.node.entity.player.info.login.PlayerSaver;
|
||||
import rs09.game.node.entity.skill.runecrafting.PouchManager;
|
||||
import rs09.game.node.entity.state.newsys.State;
|
||||
|
|
@ -230,11 +230,6 @@ public class Player extends Entity {
|
|||
*/
|
||||
private final SkullManager skullManager = new SkullManager(this);
|
||||
|
||||
/**
|
||||
* The grave stone manager.
|
||||
*/
|
||||
private final GraveManager graveManager = new GraveManager(this);
|
||||
|
||||
/**
|
||||
* The familiar manager.
|
||||
*/
|
||||
|
|
@ -608,35 +603,28 @@ public class Player extends Entity {
|
|||
}
|
||||
GroundItemManager.create(new Item(526), getLocation(), k);
|
||||
final Container[] c = DeathTask.getContainers(this);
|
||||
boolean gravestone = graveManager.generateable() && getIronmanManager().getMode() != IronmanMode.ULTIMATE;
|
||||
int seconds = graveManager.getType().getDecay() * 60;
|
||||
int ticks = (1000 * seconds) / 600;
|
||||
List<GroundItem> items = new ArrayList<>(20);
|
||||
for (Item item : c[1].toArray()) {
|
||||
if (item != null) {
|
||||
GroundItem ground;
|
||||
if (item.hasItemPlugin()) {
|
||||
item = item.getPlugin().getDeathItem(item);
|
||||
}
|
||||
if (gravestone || !item.getDefinition().isTradeable()) {
|
||||
ground = new GroundItem(item, getLocation(), gravestone ? ticks + 100 : 200, this);
|
||||
} else {
|
||||
ground = new GroundItem(item.getDropItem(), getLocation(), k);
|
||||
}
|
||||
items.add(ground);
|
||||
ground.setDropper(this); //Checking for ironman mode in any circumstance for death items is inaccurate to how it works in both 2009scapes.
|
||||
GroundItemManager.create(ground);
|
||||
|
||||
boolean canCreateGrave = GraveController.allowGenerate(this);
|
||||
if (canCreateGrave) {
|
||||
Grave g = GraveController.produceGrave(GraveController.getGraveType(this));
|
||||
g.initialize(this, location, Arrays.stream(c[1].toArray()).filter(Objects::nonNull).toArray(Item[]::new)); //note: the amount of code required to filter nulls from an array in Java is atrocious.
|
||||
} else {
|
||||
for (Item item : c[1].toArray()) {
|
||||
if (item == null) continue;
|
||||
if (GraveController.shouldCrumble(item.getId()))
|
||||
continue;
|
||||
if (GraveController.shouldRelease(item.getId()))
|
||||
continue;
|
||||
item = GraveController.checkTransform(item);
|
||||
GroundItemManager.create(item, location, killer instanceof Player ? (Player) killer : this);
|
||||
}
|
||||
sendMessage(colorize("%RDue to the circumstances of your death, you do not have a grave."));
|
||||
}
|
||||
|
||||
equipment.clear();
|
||||
inventory.clear();
|
||||
inventory.addAll(c[0]);
|
||||
if (gravestone) {
|
||||
graveManager.create(ticks, items);
|
||||
sendMessages("<col=990000>Because of your current gravestone, you have "+graveManager.getType().getDecay()+" minutes to get your items and", "<col=990000>equipment back after dying in combat.");
|
||||
}
|
||||
familiarManager.dismiss();
|
||||
|
||||
}
|
||||
skullManager.setSkulled(false);
|
||||
removeAttribute("combat-time");
|
||||
|
|
@ -1215,14 +1203,6 @@ public class Player extends Entity {
|
|||
return houseManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the graveManager.
|
||||
* @return the graveManager
|
||||
*/
|
||||
public GraveManager getGraveManager() {
|
||||
return graveManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the audioManager.
|
||||
* @return the audioManager
|
||||
|
|
|
|||
|
|
@ -223,7 +223,6 @@ public final class LoginConfiguration {
|
|||
player.getPacketDispatch().sendString("Friends List - World " + GameWorld.getSettings().getWorldId(), 550, 3);
|
||||
player.getConfigManager().init();
|
||||
player.getQuestRepository().syncronizeTab(player);
|
||||
player.getGraveManager().update();
|
||||
player.getInterfaceManager().close();
|
||||
player.getEmoteManager().refresh();
|
||||
player.getInterfaceManager().openInfoBars();
|
||||
|
|
|
|||
|
|
@ -1,151 +0,0 @@
|
|||
package core.game.node.entity.player.link.grave;
|
||||
|
||||
import core.game.node.entity.npc.AbstractNPC;
|
||||
import core.game.node.entity.npc.NPC;
|
||||
import core.game.node.entity.player.Player;
|
||||
import core.game.node.entity.player.info.Rights;
|
||||
|
||||
import core.game.node.entity.player.link.HintIconManager;
|
||||
import core.game.node.entity.player.link.prayer.PrayerType;
|
||||
import core.game.node.item.GroundItem;
|
||||
import rs09.game.world.GameWorld;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Manages the players grave stone.
|
||||
* @author Vexia
|
||||
*/
|
||||
public class GraveManager {
|
||||
|
||||
/**
|
||||
* The current grave stones in the world.
|
||||
*/
|
||||
private static final Map<String, NPC> graves = new HashMap<>();
|
||||
|
||||
/**
|
||||
* The player instance.
|
||||
*/
|
||||
private final Player player;
|
||||
|
||||
/**
|
||||
* The grave type.
|
||||
*/
|
||||
private GraveType type = GraveType.MEMORIAL_PLAQUE;
|
||||
|
||||
/**
|
||||
* The current gravestone.
|
||||
*/
|
||||
private NPC grave;
|
||||
|
||||
/**
|
||||
* Constructs a new {@Code GraveManager} {@Code Object}
|
||||
* @param player the player.
|
||||
*/
|
||||
public GraveManager(Player player) {
|
||||
this.player = player;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a grave.
|
||||
* @param ticks the ticks.
|
||||
* @param items the items.
|
||||
*/
|
||||
public void create(int ticks, List<GroundItem> items) {
|
||||
if (hasGrave()) {
|
||||
grave.clear();
|
||||
player.sendMessage("Your previous gravestone has collapsed.");
|
||||
}
|
||||
NPC npc = NPC.create(type.getNpcId(), player.getLocation(), player.getName(), GameWorld.getTicks() + ticks, items, type, player.getUsername());
|
||||
npc.init();
|
||||
setGrave(npc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the players grave items.
|
||||
*/
|
||||
public void update() {
|
||||
NPC npc = graves.get(player.getName());
|
||||
if (npc != null && npc.isActive()) {
|
||||
AbstractNPC n = (AbstractNPC) npc;
|
||||
n.fireEvent("updateItems", player);
|
||||
HintIconManager.registerHintIcon(player, n);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a gravestone is generateable at this time.
|
||||
* @return {@code True} if so.
|
||||
*/
|
||||
public boolean generateable() {
|
||||
if (player.getDetails().getRights() == Rights.ADMINISTRATOR && GameWorld.getSettings().isHosted()) {
|
||||
return false;
|
||||
}
|
||||
if (player.getSkullManager().isSkulled()) {
|
||||
return false;
|
||||
}
|
||||
if (player.getInventory().itemCount() + player.getEquipment().itemCount() <= (player.getPrayer().get(PrayerType.PROTECT_ITEMS) ? 4 : 3)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the player has an active grave.
|
||||
* @return {@code True} if so.
|
||||
*/
|
||||
public boolean hasGrave() {
|
||||
return grave != null && grave.isActive();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the type.
|
||||
* @return the type
|
||||
*/
|
||||
public GraveType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the grave type.
|
||||
* @param type the type to set.
|
||||
*/
|
||||
public void setType(GraveType type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the player.
|
||||
* @return the player
|
||||
*/
|
||||
public Player getPlayer() {
|
||||
return player;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the grave.
|
||||
* @return the grave
|
||||
*/
|
||||
public NPC getGrave() {
|
||||
return grave;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the bagrave.
|
||||
* @param grave the grave to set.
|
||||
*/
|
||||
public void setGrave(NPC grave) {
|
||||
this.grave = grave;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the graves.
|
||||
* @return the graves
|
||||
*/
|
||||
public static Map<String, NPC> getGraves() {
|
||||
return graves;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,74 +0,0 @@
|
|||
package core.game.node.entity.player.link.grave;
|
||||
|
||||
/**
|
||||
* A grave stone type.
|
||||
* @author Vexia
|
||||
*/
|
||||
public enum GraveType {
|
||||
MEMORIAL_PLAQUE(0, 3, 6565, "In memory of @name, who died here."), FLAG(50, 3, 6568, "In memory of @name, who died here."), SMALL(500, 3, 6571, "In loving memory of our dear friend @name, who died in this place @mins ago."), ORNATE(5000, 4, 6574, "In loving memory of our dear friend @name, who died in this place @mins ago."), FRONT_OF_LIFE(50000, 5, 6577, "In your travels, pause awhile to remember @name, who passed away in this spot."), STELE(50000, 5, 6580, "In your travels, pause awhile to remember @name, who passed away in this spot."), SARADOMIN(50000, 5, 6583, "@name, an enlightened severant of Saradomin, perished in this place."), ZAMRAOK(50000, 5, 6586, "@name a most bloodthirsty follower of Zamorak, perished in this place."), GUTHIX(50000, 5, 6589, "@name walked with the Balance of Guthix, perished in this place."), BANDOS(50000, 5, 6592, "@name, a vicious warrior dedicated to Bandos, perished in this place."), ARMADYL(50000, 5, 6595, "@name a follower of the Law of Aramdyl, perished in this place."), MEMORIAL_STONE(50000, 5, 6598, "@name, servant of the Unknown Power, perished in this place."), ANGEL_OF_DEATH(500000, 6, 6601, "Ye frails who gaze upon this sight, forget not the date of @name, once mighty, now surrendered to the inescapable grasp of destiny, Requiescat in pace.");
|
||||
|
||||
/**
|
||||
* The cost of the grave stone.
|
||||
*/
|
||||
private final int cost;
|
||||
|
||||
/**
|
||||
* The decay time of this gravestone.
|
||||
*/
|
||||
private final int decay;
|
||||
|
||||
/**
|
||||
* The npc id of this gravestone.
|
||||
*/
|
||||
private final int npcId;
|
||||
|
||||
/**
|
||||
* The message to display on the grave.
|
||||
*/
|
||||
private final String message;
|
||||
|
||||
/**
|
||||
* Constructs a new {@code GraveType} {@code Object}.
|
||||
* @param cost the cost.
|
||||
* @param decay the decay.
|
||||
*/
|
||||
GraveType(final int cost, final int decay, final int npc, final String message) {
|
||||
this.cost = cost;
|
||||
this.decay = decay;
|
||||
this.npcId = npc;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the decay.
|
||||
* @return the decay
|
||||
*/
|
||||
public int getDecay() {
|
||||
return decay;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the cost.
|
||||
* @return the cost
|
||||
*/
|
||||
public int getCost() {
|
||||
return cost;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the npcId.
|
||||
* @return the npcId
|
||||
*/
|
||||
public int getNpcId() {
|
||||
return npcId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the message.
|
||||
* @return the message
|
||||
*/
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -15,6 +15,8 @@ public class GroundItem extends Item {
|
|||
*/
|
||||
private Player dropper;
|
||||
|
||||
private int dropperUid;
|
||||
|
||||
/**
|
||||
* The amount of ticks.
|
||||
*/
|
||||
|
|
@ -62,6 +64,12 @@ public class GroundItem extends Item {
|
|||
this(item, location, 200, player);
|
||||
}
|
||||
|
||||
public GroundItem(Item item, Location location, int playerUid, int ticks) {
|
||||
this(item, location);
|
||||
this.dropperUid = playerUid;
|
||||
this.decayTime = ticks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new {@code GroundItem} {@code Object}.
|
||||
* @param item The item.
|
||||
|
|
@ -74,6 +82,7 @@ public class GroundItem extends Item {
|
|||
super.index = -1;
|
||||
super.interaction.setDefault();
|
||||
this.dropper = player;
|
||||
this.dropperUid = player != null ? player.getDetails().getUid() : -1;
|
||||
this.ticks = GameWorld.getTicks();
|
||||
this.decayTime = ticks + decay;
|
||||
}
|
||||
|
|
@ -104,7 +113,7 @@ public class GroundItem extends Item {
|
|||
* @return {@code True} if so.
|
||||
*/
|
||||
public boolean droppedBy(Player p) {
|
||||
if (dropper != null && p.getDetails().getUid() == dropper.getDetails().getUid()) {
|
||||
if (p.getDetails().getUid() == dropperUid) {
|
||||
dropper = p;
|
||||
return true;
|
||||
}
|
||||
|
|
@ -191,6 +200,9 @@ public class GroundItem extends Item {
|
|||
this.removed = removed;
|
||||
}
|
||||
|
||||
public int getDropperUid() {
|
||||
return dropperUid;
|
||||
}
|
||||
@Override
|
||||
public String toString() {
|
||||
return "GroundItem [dropper=" + (dropper != null ? dropper.getUsername() : dropper) + ", ticks=" + ticks + ", decayTime=" + decayTime + ", remainPrivate=" + remainPrivate + ", removed=" + removed + "]";
|
||||
|
|
|
|||
|
|
@ -32,6 +32,10 @@ public final class GroundItemManager {
|
|||
return create(new GroundItem(item, location, null));
|
||||
}
|
||||
|
||||
public static GroundItem create (Item item, Location location, int playerUid, int ticks) {
|
||||
return create(new GroundItem(item, location, playerUid, ticks));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a ground item.
|
||||
* @param item the item.
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import api.ShutdownListener;
|
|||
import core.game.node.entity.player.Player;
|
||||
import rs09.Server;
|
||||
import rs09.ServerConstants;
|
||||
import rs09.ServerStore;
|
||||
import rs09.game.ai.AIRepository;
|
||||
import rs09.game.system.SystemLogger;
|
||||
import rs09.game.world.GameWorld;
|
||||
|
|
@ -54,7 +55,16 @@ public final class SystemTermination {
|
|||
}
|
||||
}
|
||||
GameWorld.getShutdownListeners().forEach(ShutdownListener::shutdown);
|
||||
GameWorld.getWorldPersists().forEach(PersistWorld::save);
|
||||
ServerStore s = null;
|
||||
for (PersistWorld wld : GameWorld.getWorldPersists()) {
|
||||
if (wld instanceof ServerStore)
|
||||
s = (ServerStore) wld;
|
||||
else
|
||||
wld.save();
|
||||
}
|
||||
//ServerStore should ***always*** save last. Fudging a race condition here :)
|
||||
if (s != null)
|
||||
s.save();
|
||||
if(ServerConstants.DATA_PATH != null)
|
||||
save(ServerConstants.DATA_PATH);
|
||||
} catch (Throwable e) {
|
||||
|
|
|
|||
|
|
@ -442,6 +442,16 @@ public final class Location extends Node {
|
|||
return "[" + x + ", " + y + ", " + z + "]";
|
||||
}
|
||||
|
||||
public static Location fromString(String locString) {
|
||||
String trimmed = locString.replace("[", "").replace("]", "");
|
||||
String[] tokens = trimmed.split(",");
|
||||
return Location.create(
|
||||
Integer.parseInt(tokens[0].trim()),
|
||||
Integer.parseInt(tokens[1].trim()),
|
||||
Integer.parseInt(tokens[2].trim())
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return z << 30 | x << 15 | y;
|
||||
|
|
|
|||
|
|
@ -2078,6 +2078,8 @@ fun registerHintIcon(player: Player, location: Location, height: Int) {
|
|||
* @param node the Node to register a hint icon for.
|
||||
*/
|
||||
fun registerHintIcon(player: Player, node: Node) {
|
||||
if (getAttribute(player, "hinticon", null) != null)
|
||||
return
|
||||
setAttribute(player, "hinticon", HintIconManager.registerHintIcon(player, node))
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,175 @@
|
|||
package rs09.game.node.entity.player.graves
|
||||
|
||||
import api.TickListener
|
||||
import api.clearHintIcon
|
||||
import api.registerHintIcon
|
||||
import api.sendMessage
|
||||
import core.game.node.entity.npc.AbstractNPC
|
||||
import core.game.node.entity.player.Player
|
||||
import core.game.node.item.GroundItem
|
||||
import core.game.node.item.GroundItemManager
|
||||
import core.game.node.item.Item
|
||||
import core.game.world.map.Location
|
||||
import core.plugin.Initializable
|
||||
import org.rs09.consts.NPCs
|
||||
import rs09.game.world.GameWorld
|
||||
import rs09.game.world.repository.Repository
|
||||
import rs09.tools.secondsToTicks
|
||||
import rs09.tools.stringtools.colorize
|
||||
import rs09.tools.ticksToSeconds
|
||||
import kotlin.properties.Delegates
|
||||
|
||||
@Initializable
|
||||
class Grave : AbstractNPC {
|
||||
lateinit var type: GraveType
|
||||
private val items = ArrayList<GroundItem>()
|
||||
var ownerUsername: String = ""
|
||||
var ownerUid: Int = -1
|
||||
|
||||
var ticksRemaining = -1
|
||||
|
||||
constructor() : super(NPCs.GRAVESTONE_6571, Location.create(0,0,0), false)
|
||||
private constructor(id: Int, location: Location) : super(id, location)
|
||||
|
||||
override fun construct(id: Int, location: Location, vararg objects: Any): AbstractNPC {
|
||||
return Grave(id, location)
|
||||
}
|
||||
|
||||
override fun getIds(): IntArray {
|
||||
return GraveType.ids
|
||||
}
|
||||
|
||||
fun configureType(type: GraveType) {
|
||||
this.type = type
|
||||
this.transform(type.npcId)
|
||||
this.ticksRemaining = secondsToTicks(type.durationMinutes * 60)
|
||||
}
|
||||
|
||||
fun initialize(player: Player, location: Location, inventory: Array<Item>) {
|
||||
if (!GraveController.allowGenerate(player))
|
||||
return
|
||||
|
||||
this.ownerUid = player.details.uid
|
||||
this.ownerUsername = player.username
|
||||
this.location = location
|
||||
this.isRespawn = false
|
||||
this.isWalks = false
|
||||
this.isNeverWalks = true
|
||||
|
||||
for (item in inventory) {
|
||||
if (GraveController.shouldRelease(item.id)) {
|
||||
sendMessage(player, "Your ${item.name.lowercase().replace("jar", "")} has escaped.")
|
||||
continue
|
||||
}
|
||||
|
||||
if (GraveController.shouldCrumble(item.id)) {
|
||||
sendMessage(player, "Your ${item.name.lowercase()} has crumbled to dust.")
|
||||
continue
|
||||
}
|
||||
|
||||
val finalItem = GraveController.checkTransform(item)
|
||||
|
||||
val gi = GroundItemManager.create(finalItem, this.location, player)
|
||||
gi.isRemainPrivate = true
|
||||
gi.decayTime = secondsToTicks(type.durationMinutes * 60)
|
||||
this.items.add(gi)
|
||||
}
|
||||
|
||||
if (items.isEmpty()) {
|
||||
clear()
|
||||
return
|
||||
}
|
||||
|
||||
this.init()
|
||||
|
||||
if (GraveController.activeGraves[ownerUid] != null) {
|
||||
val oldGrave = GraveController.activeGraves[ownerUid]
|
||||
oldGrave?.collapse()
|
||||
}
|
||||
|
||||
GraveController.activeGraves[ownerUid] = this
|
||||
sendMessage(player, colorize("%RBecause of your current gravestone, you have ${type.durationMinutes} minutes to get your items back."))
|
||||
}
|
||||
|
||||
fun setupFromJsonParams(playerUid: Int, ticks: Int, location: Location, items: Array<Item>, username: String) {
|
||||
this.ownerUid = playerUid
|
||||
this.ticksRemaining = ticks
|
||||
this.location = location
|
||||
this.isRespawn = false
|
||||
this.isWalks = false
|
||||
this.isNeverWalks = true
|
||||
this.ownerUsername = username
|
||||
|
||||
for (item in items) {
|
||||
val gi = GroundItemManager.create(item, location, playerUid, GameWorld.ticks + ticksRemaining)
|
||||
gi.isRemainPrivate = true
|
||||
this.items.add(gi)
|
||||
}
|
||||
|
||||
this.transform(type.npcId)
|
||||
this.init()
|
||||
}
|
||||
|
||||
override fun tick() {
|
||||
//Grave should not do anything else on tick, that is all handled by GraveController.
|
||||
if (Repository.uid_map[ownerUid] != null) {
|
||||
val p = Repository.uid_map[ownerUid] ?: return
|
||||
registerHintIcon(p, this)
|
||||
}
|
||||
}
|
||||
|
||||
fun addTime(ticks: Int) {
|
||||
ticksRemaining += ticks
|
||||
for (gi in items) {
|
||||
gi.decayTime = ticksRemaining
|
||||
}
|
||||
if (ticksRemaining < 30)
|
||||
transform(type.npcId + 2)
|
||||
else if (ticksRemaining < 90)
|
||||
transform(type.npcId + 1)
|
||||
else
|
||||
transform(type.npcId)
|
||||
}
|
||||
|
||||
fun collapse() {
|
||||
for (item in items) {
|
||||
GroundItemManager.destroy(item)
|
||||
}
|
||||
clear()
|
||||
GraveController.activeGraves.remove(ownerUid)
|
||||
if (Repository.uid_map[ownerUid] != null) {
|
||||
val p = Repository.uid_map[ownerUid] ?: return
|
||||
clearHintIcon(p)
|
||||
}
|
||||
}
|
||||
|
||||
fun demolish() {
|
||||
val owner = Repository.uid_map[ownerUid] ?: return
|
||||
for (item in items) {
|
||||
if (!item.isRemoved)
|
||||
item.decayTime = secondsToTicks(45)
|
||||
}
|
||||
clear()
|
||||
sendMessage(owner, "It looks like it'll last another ${getFormattedTimeRemaining()}.")
|
||||
sendMessage(owner, "You demolish it anyway.")
|
||||
GraveController.activeGraves.remove(ownerUid)
|
||||
clearHintIcon(owner)
|
||||
}
|
||||
|
||||
fun getItems() : Array<GroundItem> {
|
||||
return this.items.toTypedArray()
|
||||
}
|
||||
|
||||
fun retrieveFormattedText(): String {
|
||||
return type.text
|
||||
.replace("@name", ownerUsername)
|
||||
.replace("@mins", getFormattedTimeRemaining())
|
||||
}
|
||||
|
||||
fun getFormattedTimeRemaining() : String {
|
||||
val seconds = ticksToSeconds(ticksRemaining)
|
||||
val timeQty = if (seconds / 60 > 0) seconds / 60 else seconds
|
||||
val timeUnit = (if (seconds / 60 > 0) "minute" else "second") + if (timeQty > 1) "s" else ""
|
||||
return "$timeQty $timeUnit"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,286 @@
|
|||
package rs09.game.node.entity.player.graves
|
||||
|
||||
import api.*
|
||||
import core.game.interaction.Interaction
|
||||
import core.game.node.Node
|
||||
import core.game.node.entity.combat.ImpactHandler
|
||||
import core.game.node.entity.player.Player
|
||||
import core.game.node.entity.player.info.Rights
|
||||
import core.game.node.entity.player.link.IronmanMode
|
||||
import core.game.node.entity.player.link.audio.Audio
|
||||
import core.game.node.entity.skill.Skills
|
||||
import core.game.node.item.Item
|
||||
import core.game.system.task.Pulse
|
||||
import core.game.world.map.Location
|
||||
import org.json.simple.JSONArray
|
||||
import org.json.simple.JSONObject
|
||||
import org.rs09.consts.Items
|
||||
import rs09.ServerStore
|
||||
import rs09.game.interaction.InteractionListener
|
||||
import rs09.game.system.command.Privilege
|
||||
import rs09.game.world.GameWorld
|
||||
import rs09.game.world.repository.Repository
|
||||
import rs09.tools.secondsToTicks
|
||||
import rs09.tools.stringtools.colorize
|
||||
import rs09.tools.ticksToSeconds
|
||||
import java.util.Map
|
||||
import kotlin.math.min
|
||||
|
||||
class GraveController : PersistWorld, TickListener, InteractionListener, Commands {
|
||||
override fun defineListeners() {
|
||||
on(GraveType.ids, NPC, "read", handler = this::onGraveReadOption)
|
||||
on(GraveType.ids, NPC, "bless", handler = this::onGraveBlessed)
|
||||
on(GraveType.ids, NPC, "repair", handler = this::onGraveRepaired)
|
||||
on(GraveType.ids, NPC, "demolish", handler = this::onGraveDemolished)
|
||||
}
|
||||
|
||||
override fun defineCommands() {
|
||||
define("forcegravedeath", Privilege.ADMIN, "", "Forces a death that should produce a grave.") {player, _ ->
|
||||
player.details.rights = Rights.REGULAR_PLAYER
|
||||
setAttribute(player, "tutorial:complete", true)
|
||||
player.impactHandler.manualHit(player, player.skills.lifepoints, ImpactHandler.HitsplatType.NORMAL)
|
||||
notify(player, "Grave created at ${player.location}")
|
||||
GameWorld.Pulser.submit(object : Pulse(15) {
|
||||
override fun pulse(): Boolean {
|
||||
player.details.rights = Rights.ADMINISTRATOR
|
||||
sendMessage(player, "Rights restored")
|
||||
return true
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
override fun tick() {
|
||||
for (grave in activeGraves.values.toTypedArray()) {
|
||||
if (grave.ticksRemaining == -1) return
|
||||
|
||||
if (grave.ticksRemaining == secondsToTicks(30) || grave.ticksRemaining == secondsToTicks(90)) {
|
||||
grave.transform(grave.id + 1)
|
||||
}
|
||||
|
||||
if (grave.ticksRemaining == 0) {
|
||||
grave.collapse()
|
||||
}
|
||||
|
||||
grave.ticksRemaining--
|
||||
}
|
||||
}
|
||||
|
||||
private fun onGraveReadOption(player: Player, node: Node) : Boolean {
|
||||
val grave = node as? Grave ?: return false
|
||||
|
||||
var isGraniteBackground = false
|
||||
|
||||
when (grave.type) {
|
||||
in GraveType.SMALL_GS..GraveType.ANGEL_DEATH -> isGraniteBackground = true
|
||||
}
|
||||
|
||||
if (isGraniteBackground)
|
||||
setVarbit(player, 4191, 1)
|
||||
else
|
||||
setVarbit(player, 4191, 0)
|
||||
|
||||
openInterface(player, 266)
|
||||
setInterfaceText(player, grave.retrieveFormattedText(), 266, 23)
|
||||
|
||||
if (player.details.uid == grave.ownerUid) {
|
||||
sendMessage(player, "It looks like it'll survive another ${grave.getFormattedTimeRemaining()}.")
|
||||
sendMessage(player, "Isn't there something a bit odd about reading your own gravestone?")
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
private fun onGraveBlessed(player: Player, node: Node) : Boolean {
|
||||
val g = node as? Grave ?: return false
|
||||
|
||||
if (getAttribute(g, "blessed", false)) {
|
||||
sendMessage(player, "This grave has already been blessed.")
|
||||
return true
|
||||
}
|
||||
|
||||
if (player.details.uid == g.ownerUid) {
|
||||
sendMessage(player, "The gods don't seem to approve of people attempting to bless their own gravestones.")
|
||||
return true
|
||||
}
|
||||
|
||||
if (getStatLevel(player, Skills.PRAYER) < 70) {
|
||||
sendMessage(player, "You need a Prayer level of 70 to bless a grave.")
|
||||
return true
|
||||
}
|
||||
|
||||
val blessAmount = min(60, player.skills.prayerPoints.toInt() - 10)
|
||||
|
||||
if (blessAmount <= 0) {
|
||||
sendMessage(player, "You do not have enough prayer points to do that.")
|
||||
return true
|
||||
}
|
||||
|
||||
g.addTime(secondsToTicks(blessAmount * 60))
|
||||
player.skills.prayerPoints -= blessAmount
|
||||
setAttribute(g, "blessed", true)
|
||||
|
||||
playAudio(player, Audio(2674))
|
||||
animate(player, 645)
|
||||
|
||||
val gOwner = Repository.uid_map[g.ownerUid]
|
||||
if (gOwner != null) {
|
||||
sendMessage(gOwner, colorize("%RYour grave has been blessed."))
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
private fun onGraveRepaired(player: Player, node: Node) : Boolean {
|
||||
val g = node as? Grave ?: return false
|
||||
|
||||
if (getAttribute(g, "repaired", false)) {
|
||||
sendMessage(player, "This grave has already been repaired.")
|
||||
return true
|
||||
}
|
||||
|
||||
if (getStatLevel(player, Skills.PRAYER) < 2) {
|
||||
sendMessage(player, "You need a Prayer level of 2 to bless a grave.")
|
||||
return true
|
||||
}
|
||||
|
||||
if (player.skills.prayerPoints < 1.0) {
|
||||
sendMessage(player, "You do not have enough prayer points to do that.")
|
||||
return true
|
||||
}
|
||||
|
||||
val restoreAmount = min(5, player.skills.prayerPoints.toInt())
|
||||
g.addTime(secondsToTicks(restoreAmount * 60))
|
||||
player.skills.prayerPoints -= restoreAmount
|
||||
setAttribute(g, "repaired", true)
|
||||
|
||||
playAudio(player, Audio(2674))
|
||||
animate(player, 645)
|
||||
return true
|
||||
}
|
||||
|
||||
private fun onGraveDemolished(player: Player, node: Node) : Boolean {
|
||||
val g = node as? Grave ?: return false
|
||||
|
||||
if (player.details.uid != g.ownerUid) {
|
||||
sendMessage(player, "You cannot demolish someone else's gravestone!")
|
||||
return true
|
||||
}
|
||||
|
||||
g.demolish()
|
||||
return true
|
||||
}
|
||||
|
||||
override fun save() {
|
||||
serializeToServerStore()
|
||||
}
|
||||
|
||||
override fun parse() {
|
||||
deserializeFromServerStore()
|
||||
}
|
||||
|
||||
companion object {
|
||||
val activeGraves = HashMap<Int, Grave>()
|
||||
var childCounter = 0
|
||||
val ATTR_GTYPE = "/save:gravetype"
|
||||
|
||||
@JvmStatic fun produceGrave(type: GraveType): Grave {
|
||||
val g = Grave()
|
||||
g.configureType(type)
|
||||
return g
|
||||
}
|
||||
|
||||
@JvmStatic fun shouldCrumble(item: Int) : Boolean {
|
||||
when (item) {
|
||||
Items.ECTOPHIAL_4251 -> return true
|
||||
in Items.SMALL_POUCH_5509..Items.GIANT_POUCH_5515 -> return true
|
||||
}
|
||||
|
||||
return itemDefinition(item).hasAction("destroy")
|
||||
}
|
||||
|
||||
@JvmStatic fun shouldRelease(item: Int) : Boolean {
|
||||
when (item) {
|
||||
Items.CHINCHOMPA_9976 -> return true
|
||||
Items.CHINCHOMPA_10033 -> return true
|
||||
in Items.BABY_IMPLING_JAR_11238..Items.DRAGON_IMPLING_JAR_11257 -> return itemDefinition(item).isUnnoted
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
@JvmStatic fun checkTransform(item: Item) : Item {
|
||||
if (item.hasItemPlugin())
|
||||
return item.plugin.getDeathItem(item)
|
||||
return item
|
||||
}
|
||||
|
||||
@JvmStatic fun allowGenerate(player: Player) : Boolean {
|
||||
if (player.skullManager.isSkulled)
|
||||
return false
|
||||
if (player.skullManager.isWilderness)
|
||||
return false
|
||||
if (player.ironmanManager.mode == IronmanMode.HARDCORE)
|
||||
return false
|
||||
return true
|
||||
}
|
||||
|
||||
@JvmStatic fun getGraveType(player: Player) : GraveType {
|
||||
return GraveType.values()[getAttribute(player, ATTR_GTYPE, 0)]
|
||||
}
|
||||
|
||||
@JvmStatic fun updateGraveType(player: Player, type: GraveType) {
|
||||
setAttribute(player, ATTR_GTYPE, type.ordinal)
|
||||
}
|
||||
|
||||
@JvmStatic fun hasGraveAt(loc: Location) : Boolean {
|
||||
return activeGraves.values.toTypedArray().any { it.location == loc }
|
||||
}
|
||||
|
||||
fun serializeToServerStore() {
|
||||
val archive = ServerStore.getArchive("active-graves")
|
||||
for ((uid,grave) in activeGraves) {
|
||||
val g = JSONObject()
|
||||
g["ticksRemaining"] = grave.ticksRemaining
|
||||
g["location"] = grave.location.toString()
|
||||
g["type"] = grave.type.ordinal
|
||||
g["username"] = grave.ownerUsername
|
||||
val items = JSONArray()
|
||||
for (item in grave.getItems()) {
|
||||
val i = JSONObject()
|
||||
i["id"] = item.id
|
||||
i["amount"] = item.amount
|
||||
i["charge"] = item.charge
|
||||
items.add(i)
|
||||
}
|
||||
g["items"] = items
|
||||
archive["$uid"] = g
|
||||
}
|
||||
}
|
||||
|
||||
fun deserializeFromServerStore() {
|
||||
val archive = ServerStore.getArchive("active-graves")
|
||||
for (entry in archive.entries as Set<Map.Entry<String, JSONObject>>) {
|
||||
val g = entry.value as JSONObject
|
||||
val uid = (entry.key as String).toInt()
|
||||
val type = g["type"].toString().toInt()
|
||||
val ticks = g["ticksRemaining"].toString().toInt()
|
||||
val location = Location.fromString(g["location"].toString())
|
||||
val username = g["username"].toString()
|
||||
|
||||
val items = ArrayList<Item>()
|
||||
val itemsRaw = g["items"] as JSONArray
|
||||
for (itemRaw in itemsRaw) {
|
||||
val item = itemRaw as JSONObject
|
||||
val id = item["id"].toString().toInt()
|
||||
val amount = item["amount"].toString().toInt()
|
||||
val charge = item["charge"].toString().toInt()
|
||||
items.add(Item(id, amount, charge))
|
||||
}
|
||||
|
||||
val grave = produceGrave(GraveType.values()[type])
|
||||
grave.setupFromJsonParams(uid, ticks, location, items.toTypedArray(), username)
|
||||
activeGraves[uid] = grave
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
package rs09.game.node.entity.player.graves
|
||||
|
||||
import api.*
|
||||
import core.game.node.item.Item
|
||||
import org.rs09.consts.Components
|
||||
import org.rs09.consts.Items
|
||||
import rs09.game.interaction.InterfaceListener
|
||||
|
||||
class GravePurchaseInterface : InterfaceListener {
|
||||
val BUTTON_CONFIRM = 34
|
||||
val AVAILABLE_GRAVES_BITFIELD = 0xFFF //Enable all graves, authentically the quest-locked ones should be excluded from the bitfield, but y'know.
|
||||
val AVAILABLE_GRAVES_VARBIT = 4191
|
||||
val CURRENT_GRAVE_VARBIT = 4190
|
||||
|
||||
override fun defineInterfaceListeners() {
|
||||
onOpen (Components.GRAVESTONE_SHOP_652) {player, _ ->
|
||||
val userType = GraveController.getGraveType(player).ordinal
|
||||
setVarbit(player, AVAILABLE_GRAVES_VARBIT, AVAILABLE_GRAVES_BITFIELD)
|
||||
setVarbit(player, CURRENT_GRAVE_VARBIT, userType)
|
||||
|
||||
val settings = IfaceSettingsBuilder()
|
||||
.enableAllOptions()
|
||||
.build()
|
||||
player.packetDispatch.sendIfaceSettings(settings, 34, Components.GRAVESTONE_SHOP_652, 0, 13)
|
||||
return@onOpen true
|
||||
}
|
||||
|
||||
on (Components.GRAVESTONE_SHOP_652, BUTTON_CONFIRM) { player, _, _, _, slot, _ ->
|
||||
val selectedType = GraveType.values()[slot]
|
||||
val userType = GraveController.getGraveType(player)
|
||||
val activeGrave = GraveController.activeGraves[player.details.uid]
|
||||
|
||||
if (activeGrave != null){
|
||||
sendDialogue(player, "You cannot change graves while you have a grave active.")
|
||||
return@on true
|
||||
}
|
||||
|
||||
if (selectedType == userType) {
|
||||
sendDialogue(player, "You already have that gravestone!")
|
||||
return@on true
|
||||
}
|
||||
|
||||
val cost = selectedType.cost
|
||||
val requirement = selectedType.requiredQuest
|
||||
|
||||
if (requirement.isNotEmpty() && !isQuestComplete(player, requirement)) {
|
||||
sendDialogue(player, "That gravestone requires completion of $requirement.")
|
||||
return@on true
|
||||
}
|
||||
|
||||
if (selectedType != GraveType.MEM_PLAQUE && amountInInventory(player, Items.COINS_995) < cost) {
|
||||
sendDialogue(player, "You do not have enough coins to afford that gravestone.")
|
||||
return@on true
|
||||
}
|
||||
|
||||
if (selectedType == GraveType.MEM_PLAQUE || removeItem(player, Item(995, cost))) {
|
||||
GraveController.updateGraveType(player, selectedType)
|
||||
sendDialogue(player, "Your grave has been updated.")
|
||||
}
|
||||
|
||||
closeInterface(player)
|
||||
return@on true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
package rs09.game.node.entity.player.graves
|
||||
|
||||
import org.rs09.consts.NPCs
|
||||
|
||||
enum class GraveType(val npcId: Int, val cost: Int, val durationMinutes: Int, val isMembers: Boolean, val requiredQuest: String = "", val text: String) {
|
||||
MEM_PLAQUE(NPCs.GRAVE_MARKER_6565, 0, 2, false, text = "In memory of @name,<br>who died here."),
|
||||
FLAG(NPCs.GRAVE_MARKER_6568, 50, 2, false, text = MEM_PLAQUE.text),
|
||||
SMALL_GS(NPCs.GRAVESTONE_6571, 500, 2, false, text = "In loving memory of our dear friend @name,<br>who died in this place @mins ago."),
|
||||
ORNATE_GS(NPCs.GRAVESTONE_6574, 5000, 3, false, text = SMALL_GS.text),
|
||||
FONT_OF_LIFE(NPCs.GRAVESTONE_6577, 50000, 4, true, text = "In your travels,<br>pause awhile to remember @name,<br>who passed away at this spot."),
|
||||
STELE(NPCs.STELE_6580, 50000, 4, true, text = FONT_OF_LIFE.text),
|
||||
SARA_SYMBOL(NPCs.SARADOMIN_SYMBOL_6583, 50000, 4, true, text = "@name,<br>an enlightened servant of Saradomin,<br>perished in this place."),
|
||||
ZAM_SYMBOL(NPCs.ZAMORAK_SYMBOL_6586, 50000, 4, true, text = "@name,<br>a most bloodthirsty follower of Zamorak,<br>perished in this place."),
|
||||
GUTH_SYMBOL(NPCs.GUTHIX_SYMBOL_6589, 50000, 4, true, text = "@name,<br>who walked with the Balance of Guthix,<br>perished in this place."),
|
||||
BAND_SYMBOL(NPCs.BANDOS_SYMBOL_6592, 50000, 4, true, requiredQuest = "Land of the Goblins", text = "@name,<br>a vicious warrior dedicated to Bandos,<br>perished in this place. "),
|
||||
ARMA_SYMBOL(NPCs.ARMADYL_SYMBOL_6595, 50000, 4, true, requiredQuest = "Temple of Ikov", text = "@name,<br>a follower of the Law of Armadyl,<br>perished in this place."),
|
||||
ZARO_SYMBOL(NPCs.MEMORIAL_STONE_6598, 50000, 4, true, requiredQuest = "Desert Treasure", text = "@name,<br>servant of the Unknown Power,<br>perished in this place."),
|
||||
ANGEL_DEATH(NPCs.MEMORIAL_STONE_6601, 500000, 5, true, text = "Ye frail mortals who gaze upon this sight,<br>forget not the fate of @name, once mighty, now<br>surrendered to the inescapable grasp of destiny.<br><i>Requiescat in pace.</i>");
|
||||
|
||||
companion object {
|
||||
val ids = values().fold(ArrayList<Int>()) {list, type ->
|
||||
list.add(type.npcId)
|
||||
list.add(type.npcId + 1)
|
||||
list.add(type.npcId + 2)
|
||||
list
|
||||
}.toIntArray()
|
||||
}
|
||||
}
|
||||
|
|
@ -1,20 +1,18 @@
|
|||
package rs09.game.node.entity.player.info.login
|
||||
|
||||
import api.PersistPlayer
|
||||
import core.game.interaction.item.brawling_gloves.BrawlingGloves
|
||||
import core.game.node.entity.combat.CombatSpell
|
||||
import core.game.node.entity.player.Player
|
||||
import core.game.node.entity.player.link.IronmanMode
|
||||
import core.game.node.entity.player.link.SpellBookManager
|
||||
import core.game.node.entity.player.link.emote.Emotes
|
||||
import core.game.node.entity.player.link.grave.GraveType
|
||||
import core.game.node.entity.player.link.music.MusicEntry
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import org.json.simple.JSONArray
|
||||
import org.json.simple.JSONObject
|
||||
import org.json.simple.parser.JSONParser
|
||||
import rs09.ServerConstants
|
||||
import rs09.game.node.entity.player.graves.GraveController
|
||||
import rs09.game.node.entity.player.graves.GraveType
|
||||
import rs09.game.node.entity.skill.farming.CompostBins
|
||||
import rs09.game.node.entity.skill.farming.FarmingPatch
|
||||
import rs09.game.system.SystemLogger
|
||||
|
|
@ -287,8 +285,9 @@ class PlayerSaveParser(val player: Player) {
|
|||
|
||||
fun parseGrave() {
|
||||
saveFile ?: return
|
||||
val graveData = (saveFile!!["grave_type"] as String).toInt()
|
||||
player.graveManager.type = GraveType.values()[graveData]
|
||||
val graveData = (saveFile!!["grave_type"] as? String)?.toInt() ?: return
|
||||
val type = GraveType.values()[graveData]
|
||||
GraveController.updateGraveType(player, type)
|
||||
}
|
||||
|
||||
fun parseAppearance() {
|
||||
|
|
|
|||
|
|
@ -39,7 +39,6 @@ class PlayerSaver (val player: Player){
|
|||
saveAppearance(saveFile)
|
||||
saveSpellbook(saveFile)
|
||||
saveVarps(saveFile)
|
||||
saveGraveType(saveFile)
|
||||
saveSavedData(saveFile)
|
||||
saveAutocast(saveFile)
|
||||
saveConfigs(saveFile)
|
||||
|
|
@ -517,10 +516,6 @@ class PlayerSaver (val player: Player){
|
|||
root.put("activityData",activityData)
|
||||
}
|
||||
|
||||
fun saveGraveType(root: JSONObject){
|
||||
root.put("grave_type",player.graveManager.type.ordinal.toString())
|
||||
}
|
||||
|
||||
fun saveSpellbook(root: JSONObject){
|
||||
root.put("spellbook",player.spellBookManager.spellBook.toString())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import core.game.world.map.RegionManager
|
|||
import core.plugin.CorePluginTypes.StartupPlugin
|
||||
import core.tools.RandomFunction
|
||||
import rs09.ServerConstants
|
||||
import rs09.ServerStore
|
||||
import rs09.auth.AuthProvider
|
||||
import rs09.game.system.Auth
|
||||
import rs09.game.system.SystemLogger
|
||||
|
|
@ -169,7 +170,9 @@ object GameWorld {
|
|||
ConfigParser().prePlugin()
|
||||
ClassScanner.scanClasspath()
|
||||
ClassScanner.loadPureInterfaces()
|
||||
worldPersists.forEach { it.parse() }
|
||||
val s = worldPersists.filterIsInstance<ServerStore>().first()
|
||||
s.parse()
|
||||
worldPersists.filter { it !is ServerStore }.forEach { it.parse() }
|
||||
ClassScanner.loadSideEffectfulPlugins()
|
||||
configParser.postPlugin()
|
||||
startupListeners.forEach { it.startup() }
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ class PulseRunner {
|
|||
fun updateAll() {
|
||||
val pulseCount = pulses.size
|
||||
|
||||
for (i in 0..pulseCount) {
|
||||
for (i in 0 until pulseCount) {
|
||||
val pulse = pulses.take()
|
||||
|
||||
val elapsedTime = measure {
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import core.cache.crypto.ISAACCipher
|
|||
import core.cache.crypto.ISAACPair
|
||||
import core.game.node.entity.player.Player
|
||||
import core.game.node.entity.player.info.PlayerDetails
|
||||
import core.game.node.entity.player.info.Rights
|
||||
import core.game.node.entity.player.link.IronmanMode
|
||||
import core.game.node.item.Item
|
||||
import core.net.IoSession
|
||||
|
|
@ -20,12 +21,16 @@ import rs09.game.world.update.UpdateSequence
|
|||
import java.nio.ByteBuffer
|
||||
|
||||
object TestUtils {
|
||||
fun getMockPlayer(name: String, ironman: IronmanMode = IronmanMode.NONE): Player {
|
||||
var uidCounter = 0
|
||||
|
||||
fun getMockPlayer(name: String, ironman: IronmanMode = IronmanMode.NONE, rights: Rights = Rights.ADMINISTRATOR): Player {
|
||||
val p = MockPlayer(name)
|
||||
p.ironmanManager.mode = ironman
|
||||
p.details.accountInfo.uid = uidCounter++
|
||||
Repository.addPlayer(p)
|
||||
//Update sequence has a separate list of players for some reason...
|
||||
UpdateSequence.renderablePlayers.add(p)
|
||||
p.details.rights = rights
|
||||
return p
|
||||
}
|
||||
|
||||
|
|
|
|||
313
Server/src/test/kotlin/content/DeathTests.kt
Normal file
313
Server/src/test/kotlin/content/DeathTests.kt
Normal file
|
|
@ -0,0 +1,313 @@
|
|||
package content
|
||||
|
||||
import TestUtils
|
||||
import api.asItem
|
||||
import core.game.content.global.action.DropItemHandler
|
||||
import core.game.node.entity.combat.ImpactHandler
|
||||
import core.game.node.entity.player.info.Rights
|
||||
import core.game.node.entity.player.link.IronmanMode
|
||||
import core.game.world.map.Location
|
||||
import org.junit.Assert
|
||||
import org.junit.BeforeClass
|
||||
import org.junit.jupiter.api.Assertions
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.parallel.Execution
|
||||
import org.junit.jupiter.api.parallel.ExecutionMode
|
||||
import org.rs09.consts.Items
|
||||
import rs09.game.node.entity.player.graves.GraveType
|
||||
import rs09.game.node.entity.player.graves.GraveController
|
||||
import rs09.game.world.GameWorld
|
||||
import rs09.tools.secondsToTicks
|
||||
class DeathTests {
|
||||
init {
|
||||
//explicitly register the GraveController as a tick listener because tests don't run reflection
|
||||
TestUtils.preTestSetup() // need cache parsed to properly evaluate item values
|
||||
GameWorld.tickListeners.add(GraveController())
|
||||
}
|
||||
//Grave requirements source: https://runescape.wiki/w/Gravestone?oldid=854455
|
||||
|
||||
@Test fun graveUtilsProduceGraveShouldProduceCorrectGrave() {
|
||||
val type = GraveType.MEM_PLAQUE
|
||||
val grave = GraveController.produceGrave(type)
|
||||
|
||||
Assertions.assertEquals(type, grave.type)
|
||||
}
|
||||
|
||||
@Test fun graveInitializedWithItemsShouldInitializeCorrectly() {
|
||||
val inventory = arrayOf(
|
||||
Items.RUNE_SCIMITAR_1333.asItem(),
|
||||
Items.RUNE_SCIMITAR_1333.asItem(),
|
||||
Items.RUNE_SCIMITAR_1333.asItem(),
|
||||
Items.YELLOW_BEAD_1472.asItem()
|
||||
)
|
||||
val player = TestUtils.getMockPlayer("gravetest", IronmanMode.NONE, Rights.REGULAR_PLAYER)
|
||||
|
||||
val grave = GraveController.produceGrave(GraveType.MEM_PLAQUE)
|
||||
grave.initialize(player, Location.create(0,0,0), inventory)
|
||||
|
||||
for (item in grave.getItems()) {
|
||||
Assertions.assertEquals(player, item.dropper)
|
||||
Assertions.assertEquals(player.details.uid, item.dropperUid)
|
||||
Assertions.assertEquals(
|
||||
GameWorld.ticks + secondsToTicks(GraveType.MEM_PLAQUE.durationMinutes * 60),
|
||||
item.decayTime
|
||||
)
|
||||
Assertions.assertEquals(true, item.isRemainPrivate)
|
||||
Assertions.assertEquals(true, item.id in inventory.map { it.id }.toIntArray())
|
||||
Assertions.assertEquals(true, grave.isActive)
|
||||
}
|
||||
}
|
||||
|
||||
@Test fun graveInitializedWithNoItemsShouldNotSpawn() {
|
||||
val grave = GraveController.produceGrave(GraveType.MEM_PLAQUE)
|
||||
val p = TestUtils.getMockPlayer("gravetest")
|
||||
|
||||
grave.initialize(p, Location.create(0,0,0), arrayOf())
|
||||
Assertions.assertEquals(false, grave.isActive)
|
||||
}
|
||||
|
||||
@Test fun graveInitializedWithEctophialAndPouchesShouldNotKeepThem() {
|
||||
val inventory = arrayOf(
|
||||
Items.ECTOPHIAL_4251.asItem(),
|
||||
Items.SMALL_POUCH_5509.asItem(),
|
||||
Items.MEDIUM_POUCH_5510.asItem(),
|
||||
Items.MEDIUM_POUCH_5511.asItem(),
|
||||
Items.LARGE_POUCH_5512.asItem(),
|
||||
Items.LARGE_POUCH_5513.asItem(),
|
||||
Items.GIANT_POUCH_5514.asItem(),
|
||||
Items.GIANT_POUCH_5515.asItem()
|
||||
)
|
||||
val p = TestUtils.getMockPlayer("gravetest")
|
||||
val grave = GraveController.produceGrave(GraveType.MEM_PLAQUE)
|
||||
grave.initialize(p, Location.create(0,0,0), inventory)
|
||||
|
||||
Assertions.assertEquals(true, grave.getItems().isEmpty())
|
||||
Assertions.assertEquals(false, grave.isActive)
|
||||
}
|
||||
|
||||
@Test fun graveInitializedWithDroppableUntradablesShouldKeepThem() {
|
||||
val inventory = arrayOf(
|
||||
Items.FIRE_CAPE_6570.asItem(),
|
||||
Items.RUNE_DEFENDER_8850.asItem()
|
||||
)
|
||||
val p = TestUtils.getMockPlayer("gravetest")
|
||||
val grave = GraveController.produceGrave(GraveType.MEM_PLAQUE)
|
||||
grave.initialize(p, Location.create(0,0,0), inventory)
|
||||
|
||||
Assertions.assertEquals(2, grave.getItems().size)
|
||||
Assertions.assertEquals(true, grave.isActive)
|
||||
}
|
||||
|
||||
@Test fun graveInitializedWithDestroyableItemsShouldNotKeepThem() {
|
||||
val inventory = arrayOf(
|
||||
Items.HOLY_GRAIL_19.asItem()
|
||||
)
|
||||
val p = TestUtils.getMockPlayer("gravetest")
|
||||
val grave = GraveController.produceGrave(GraveType.MEM_PLAQUE)
|
||||
grave.initialize(p, Location.create(0,0,0), inventory)
|
||||
|
||||
Assertions.assertEquals(0, grave.getItems().size)
|
||||
Assertions.assertEquals(false, grave.isActive)
|
||||
}
|
||||
|
||||
@Test fun graveInitializedWithReleasableItemsShouldNotKeepThem() {
|
||||
val inventory = arrayOf(
|
||||
Items.CHINCHOMPA_10033.asItem(),
|
||||
Items.CHINCHOMPA_9976.asItem(),
|
||||
Items.BABY_IMPLING_JAR_11238.asItem()
|
||||
)
|
||||
val p = TestUtils.getMockPlayer("gravetest")
|
||||
val grave = GraveController.produceGrave(GraveType.MEM_PLAQUE)
|
||||
grave.initialize(p, Location.create(0,0,0), inventory)
|
||||
|
||||
Assertions.assertEquals(0, grave.getItems().size)
|
||||
Assertions.assertEquals(false, grave.isActive)
|
||||
}
|
||||
|
||||
@Test fun graveInitializedWithItemThatHasDropTransformShouldContainTransformedItem() {
|
||||
//We actually don't have any items that have this implemented yet, but we should test it once we do.
|
||||
}
|
||||
|
||||
@Test fun graveShouldSerializeAndDeserializeFromJsonCorrectly() {
|
||||
val inventory = arrayOf(
|
||||
Items.BRONZE_2H_SWORD_1307.asItem(),
|
||||
Items.BRONZE_AXE_1351.asItem()
|
||||
)
|
||||
|
||||
val startTime = GameWorld.ticks
|
||||
val p = TestUtils.getMockPlayer("gravetest")
|
||||
val grave = GraveController.produceGrave(GraveType.MEM_PLAQUE)
|
||||
grave.initialize(p, Location.create(0,0,0), inventory)
|
||||
|
||||
TestUtils.advanceTicks(30, false)
|
||||
val expectedTicksRemaining = secondsToTicks(GraveType.MEM_PLAQUE.durationMinutes * 60) - (GameWorld.ticks - startTime)
|
||||
Assertions.assertEquals(expectedTicksRemaining, grave.ticksRemaining)
|
||||
|
||||
GraveController.serializeToServerStore()
|
||||
GraveController.activeGraves.remove(p.details.uid)
|
||||
GraveController.deserializeFromServerStore()
|
||||
|
||||
val newGrave = GraveController.activeGraves[p.details.uid]
|
||||
Assertions.assertNotNull(newGrave)
|
||||
Assertions.assertEquals(expectedTicksRemaining, newGrave?.ticksRemaining ?: -1)
|
||||
Assertions.assertEquals(2, newGrave?.getItems()?.size ?: -1)
|
||||
|
||||
val expectedItemDecayTick = GameWorld.ticks + expectedTicksRemaining
|
||||
Assertions.assertEquals(expectedItemDecayTick, newGrave?.getItems()?.get(0)?.decayTime ?: -1)
|
||||
Assertions.assertEquals(true, newGrave?.isActive)
|
||||
}
|
||||
|
||||
@Test fun regularDeathShouldSpawnGraveWithItems() {
|
||||
val inventory = arrayOf(
|
||||
Items.RUNE_SCIMITAR_1333.asItem(),
|
||||
Items.RUNE_SCIMITAR_1333.asItem(),
|
||||
Items.RUNE_SCIMITAR_1333.asItem(), //none of these should be in the grave due to having higher value
|
||||
Items.YELLOW_BEAD_1472.asItem(),
|
||||
Items.RED_BEAD_1470.asItem()
|
||||
)
|
||||
val p = TestUtils.getMockPlayer("gravetester", IronmanMode.NONE, Rights.REGULAR_PLAYER)
|
||||
p.location = Location.create(0,0,0)
|
||||
p.setAttribute("tutorial:complete", true)
|
||||
|
||||
for (item in inventory)
|
||||
p.inventory.add(item)
|
||||
|
||||
p.finalizeDeath(null)
|
||||
|
||||
val grave = GraveController.activeGraves[p.details.uid]
|
||||
Assertions.assertNotNull(grave)
|
||||
Assertions.assertEquals(2, grave?.getItems()?.size ?: -1)
|
||||
Assertions.assertEquals(false, grave?.getItems()?.map { it.id }?.contains(Items.RUNE_SCIMITAR_1333))
|
||||
}
|
||||
|
||||
@Test fun skulledDeathShouldNotSpawnGrave() {
|
||||
val inventory = arrayOf(
|
||||
Items.ABYSSAL_WHIP_4151.asItem(),
|
||||
Items.ABYSSAL_WHIP_4151.asItem(),
|
||||
Items.ABYSSAL_WHIP_4151.asItem(),
|
||||
Items.ABYSSAL_WHIP_4151.asItem(),
|
||||
Items.ABYSSAL_WHIP_4151.asItem()
|
||||
)
|
||||
val p = TestUtils.getMockPlayer("gravetester", IronmanMode.NONE, Rights.REGULAR_PLAYER)
|
||||
p.location = Location.create(0,0,0)
|
||||
p.setAttribute("tutorial:complete", true)
|
||||
|
||||
for (item in inventory)
|
||||
p.inventory.add(item)
|
||||
|
||||
p.skullManager.isSkulled = true
|
||||
p.finalizeDeath(null)
|
||||
|
||||
val grave = GraveController.activeGraves[p.details.uid]
|
||||
Assertions.assertNull(grave)
|
||||
}
|
||||
|
||||
@Test fun creatingNewGraveWithGraveAlreadyActiveShouldDestroyOldGrave() {
|
||||
val inventory1 = arrayOf(
|
||||
Items.RUNE_SCIMITAR_1333.asItem()
|
||||
)
|
||||
val grave1 = GraveController.produceGrave(GraveType.MEM_PLAQUE)
|
||||
val p = TestUtils.getMockPlayer("gravetester")
|
||||
grave1.initialize(p, Location.create(0,0,0), inventory1)
|
||||
|
||||
Assertions.assertEquals(true, grave1.isActive)
|
||||
Assertions.assertEquals(Items.RUNE_SCIMITAR_1333, GraveController.activeGraves[p.details.uid]?.getItems()?.getOrNull(0)?.id ?: -1)
|
||||
|
||||
val inventory2 = arrayOf(
|
||||
Items.ABYSSAL_WHIP_4151.asItem()
|
||||
)
|
||||
val grave2 = GraveController.produceGrave(GraveType.MEM_PLAQUE)
|
||||
grave2.initialize(p, Location.create(0,0,0), inventory2)
|
||||
|
||||
Assertions.assertEquals(false, grave1.isActive)
|
||||
Assertions.assertEquals(true, grave2.isActive)
|
||||
Assertions.assertEquals(Items.ABYSSAL_WHIP_4151, GraveController.activeGraves[p.details.uid]?.getItems()?.getOrNull(0)?.id ?: -1)
|
||||
}
|
||||
|
||||
@Test fun deathWithOnly3ItemsShouldNotProduceAGrave() {
|
||||
val inventory = arrayOf(
|
||||
Items.RUNE_SCIMITAR_1333.asItem(),
|
||||
Items.RUNE_SCIMITAR_1333.asItem(),
|
||||
Items.RUNE_SCIMITAR_1333.asItem()
|
||||
)
|
||||
val p = TestUtils.getMockPlayer("gtester", IronmanMode.NONE, Rights.REGULAR_PLAYER)
|
||||
p.location = Location.create(0,0,0)
|
||||
p.setAttribute("tutorial:complete", true)
|
||||
|
||||
for (item in inventory)
|
||||
p.inventory.add(item)
|
||||
|
||||
p.finalizeDeath(null)
|
||||
|
||||
Assertions.assertNull(GraveController.activeGraves[p.details.uid])
|
||||
}
|
||||
|
||||
@Test fun deathInsideWildernessShouldNotProduceAGrave() {
|
||||
val inventory = arrayOf(
|
||||
Items.RUNE_SCIMITAR_1333.asItem(),
|
||||
Items.RUNE_SCIMITAR_1333.asItem(),
|
||||
Items.RUNE_SCIMITAR_1333.asItem(),
|
||||
Items.RUNE_SCIMITAR_1333.asItem(),
|
||||
Items.RUNE_SCIMITAR_1333.asItem()
|
||||
)
|
||||
val p = TestUtils.getMockPlayer("tester3333", IronmanMode.NONE, Rights.REGULAR_PLAYER)
|
||||
p.skullManager.isWilderness = true
|
||||
|
||||
for (item in inventory) {
|
||||
p.inventory.add(item)
|
||||
}
|
||||
|
||||
p.finalizeDeath(null)
|
||||
|
||||
Assertions.assertNull(GraveController.activeGraves[p.details.uid])
|
||||
}
|
||||
|
||||
@Test fun deathWithRFDGlovesShouldKeepRFDGloves() {
|
||||
val inventory = arrayOf(
|
||||
Items.GLOVES_7453.asItem(),
|
||||
Items.GLOVES_7454.asItem(),
|
||||
Items.GLOVES_7455.asItem(),
|
||||
Items.GLOVES_7456.asItem(),
|
||||
Items.GLOVES_7457.asItem(),
|
||||
Items.GLOVES_7458.asItem(),
|
||||
Items.GLOVES_7459.asItem(),
|
||||
Items.GLOVES_7460.asItem(),
|
||||
Items.GLOVES_7461.asItem(),
|
||||
Items.GLOVES_7462.asItem()
|
||||
)
|
||||
val p = TestUtils.getMockPlayer("glovetest", IronmanMode.NONE, Rights.REGULAR_PLAYER)
|
||||
|
||||
for(item in inventory)
|
||||
p.inventory.add(item)
|
||||
|
||||
p.finalizeDeath(null)
|
||||
|
||||
val g = GraveController.activeGraves[p.details.uid]
|
||||
Assertions.assertNotNull(g)
|
||||
Assertions.assertEquals(7, g?.getItems()?.size ?: -1)
|
||||
}
|
||||
|
||||
@Test fun shouldNotBeAbleToDropItemOnGrave() {
|
||||
val inventory = arrayOf(
|
||||
Items.RUNE_SCIMITAR_1333.asItem(),
|
||||
Items.RUNE_SCIMITAR_1333.asItem(),
|
||||
Items.RUNE_SCIMITAR_1333.asItem(),
|
||||
Items.RUNE_SCIMITAR_1333.asItem()
|
||||
)
|
||||
val p = TestUtils.getMockPlayer("droptest", IronmanMode.NONE, Rights.REGULAR_PLAYER)
|
||||
|
||||
for (item in inventory)
|
||||
p.inventory.add(item)
|
||||
|
||||
p.finalizeDeath(null)
|
||||
p.inventory.add(Items.RUNE_SCIMITAR_1333.asItem())
|
||||
|
||||
val g = GraveController.activeGraves[p.details.uid]
|
||||
|
||||
Assertions.assertNotNull(g)
|
||||
Assertions.assertEquals(p.location, g?.location)
|
||||
|
||||
val canDrop = DropItemHandler.drop(p, p.inventory[0])
|
||||
Assertions.assertEquals(false, canDrop)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue