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:
@@ -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.entity.player.link.audio.Audio;
|
||||||
import core.game.node.item.GroundItemManager;
|
import core.game.node.item.GroundItemManager;
|
||||||
import core.game.node.item.Item;
|
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.SystemLogger;
|
||||||
import rs09.game.system.config.ItemConfigParser;
|
import rs09.game.system.config.ItemConfigParser;
|
||||||
import rs09.game.world.GameWorld;
|
import rs09.game.world.GameWorld;
|
||||||
@@ -42,6 +44,10 @@ public final class DropItemHandler {
|
|||||||
player.getDialogueInterpreter().open(9878, item);
|
player.getDialogueInterpreter().open(9878, item);
|
||||||
return true;
|
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()) {
|
if (player.getAttribute("equipLock:" + item.getId(), 0) > GameWorld.getTicks()) {
|
||||||
SystemLogger.logAlert(player + ", tried to do the drop & equip dupe.");
|
SystemLogger.logAlert(player + ", tried to do the drop & equip dupe.");
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
-2
@@ -237,8 +237,6 @@ public final class FatherAereckDialogue extends DialoguePlugin {
|
|||||||
case 10:
|
case 10:
|
||||||
end();
|
end();
|
||||||
player.getInterfaceManager().open(new Component(652));
|
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);
|
player.getAchievementDiaryManager().finishTask(player, DiaryType.LUMBRIDGE, 0, 15);
|
||||||
break;
|
break;
|
||||||
case 20:
|
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.audio.AudioManager;
|
||||||
import core.game.node.entity.player.link.diary.AchievementDiaryManager;
|
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.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.music.MusicPlayer;
|
||||||
import core.game.node.entity.player.link.prayer.Prayer;
|
import core.game.node.entity.player.link.prayer.Prayer;
|
||||||
import core.game.node.entity.player.link.prayer.PrayerType;
|
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.Skills;
|
||||||
import core.game.node.entity.skill.construction.HouseManager;
|
import core.game.node.entity.skill.construction.HouseManager;
|
||||||
import core.game.node.entity.skill.summoning.familiar.FamiliarManager;
|
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.GroundItemManager;
|
||||||
import core.game.node.item.Item;
|
import core.game.node.item.Item;
|
||||||
import core.game.system.communication.CommunicationInfo;
|
import core.game.system.communication.CommunicationInfo;
|
||||||
@@ -65,13 +63,15 @@ import kotlin.Unit;
|
|||||||
import kotlin.jvm.functions.Function1;
|
import kotlin.jvm.functions.Function1;
|
||||||
import org.rs09.consts.Items;
|
import org.rs09.consts.Items;
|
||||||
import proto.management.ClanLeaveNotification;
|
import proto.management.ClanLeaveNotification;
|
||||||
import proto.management.LeaveClanRequest;
|
|
||||||
import proto.management.PlayerStatusUpdate;
|
import proto.management.PlayerStatusUpdate;
|
||||||
import rs09.GlobalStats;
|
import rs09.GlobalStats;
|
||||||
import rs09.ServerConstants;
|
import rs09.ServerConstants;
|
||||||
import rs09.game.VarpManager;
|
import rs09.game.VarpManager;
|
||||||
import rs09.game.node.entity.combat.CombatSwingHandler;
|
import rs09.game.node.entity.combat.CombatSwingHandler;
|
||||||
import rs09.game.node.entity.combat.equipment.EquipmentDegrader;
|
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.player.info.login.PlayerSaver;
|
||||||
import rs09.game.node.entity.skill.runecrafting.PouchManager;
|
import rs09.game.node.entity.skill.runecrafting.PouchManager;
|
||||||
import rs09.game.node.entity.state.newsys.State;
|
import rs09.game.node.entity.state.newsys.State;
|
||||||
@@ -230,11 +230,6 @@ public class Player extends Entity {
|
|||||||
*/
|
*/
|
||||||
private final SkullManager skullManager = new SkullManager(this);
|
private final SkullManager skullManager = new SkullManager(this);
|
||||||
|
|
||||||
/**
|
|
||||||
* The grave stone manager.
|
|
||||||
*/
|
|
||||||
private final GraveManager graveManager = new GraveManager(this);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The familiar manager.
|
* The familiar manager.
|
||||||
*/
|
*/
|
||||||
@@ -608,35 +603,28 @@ public class Player extends Entity {
|
|||||||
}
|
}
|
||||||
GroundItemManager.create(new Item(526), getLocation(), k);
|
GroundItemManager.create(new Item(526), getLocation(), k);
|
||||||
final Container[] c = DeathTask.getContainers(this);
|
final Container[] c = DeathTask.getContainers(this);
|
||||||
boolean gravestone = graveManager.generateable() && getIronmanManager().getMode() != IronmanMode.ULTIMATE;
|
|
||||||
int seconds = graveManager.getType().getDecay() * 60;
|
boolean canCreateGrave = GraveController.allowGenerate(this);
|
||||||
int ticks = (1000 * seconds) / 600;
|
if (canCreateGrave) {
|
||||||
List<GroundItem> items = new ArrayList<>(20);
|
Grave g = GraveController.produceGrave(GraveController.getGraveType(this));
|
||||||
for (Item item : c[1].toArray()) {
|
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.
|
||||||
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 {
|
} else {
|
||||||
ground = new GroundItem(item.getDropItem(), getLocation(), k);
|
for (Item item : c[1].toArray()) {
|
||||||
}
|
if (item == null) continue;
|
||||||
items.add(ground);
|
if (GraveController.shouldCrumble(item.getId()))
|
||||||
ground.setDropper(this); //Checking for ironman mode in any circumstance for death items is inaccurate to how it works in both 2009scapes.
|
continue;
|
||||||
GroundItemManager.create(ground);
|
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();
|
equipment.clear();
|
||||||
inventory.clear();
|
inventory.clear();
|
||||||
inventory.addAll(c[0]);
|
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();
|
familiarManager.dismiss();
|
||||||
|
|
||||||
}
|
}
|
||||||
skullManager.setSkulled(false);
|
skullManager.setSkulled(false);
|
||||||
removeAttribute("combat-time");
|
removeAttribute("combat-time");
|
||||||
@@ -1215,14 +1203,6 @@ public class Player extends Entity {
|
|||||||
return houseManager;
|
return houseManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the graveManager.
|
|
||||||
* @return the graveManager
|
|
||||||
*/
|
|
||||||
public GraveManager getGraveManager() {
|
|
||||||
return graveManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the audioManager.
|
* Gets the audioManager.
|
||||||
* @return 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.getPacketDispatch().sendString("Friends List - World " + GameWorld.getSettings().getWorldId(), 550, 3);
|
||||||
player.getConfigManager().init();
|
player.getConfigManager().init();
|
||||||
player.getQuestRepository().syncronizeTab(player);
|
player.getQuestRepository().syncronizeTab(player);
|
||||||
player.getGraveManager().update();
|
|
||||||
player.getInterfaceManager().close();
|
player.getInterfaceManager().close();
|
||||||
player.getEmoteManager().refresh();
|
player.getEmoteManager().refresh();
|
||||||
player.getInterfaceManager().openInfoBars();
|
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 Player dropper;
|
||||||
|
|
||||||
|
private int dropperUid;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The amount of ticks.
|
* The amount of ticks.
|
||||||
*/
|
*/
|
||||||
@@ -62,6 +64,12 @@ public class GroundItem extends Item {
|
|||||||
this(item, location, 200, player);
|
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}.
|
* Constructs a new {@code GroundItem} {@code Object}.
|
||||||
* @param item The item.
|
* @param item The item.
|
||||||
@@ -74,6 +82,7 @@ public class GroundItem extends Item {
|
|||||||
super.index = -1;
|
super.index = -1;
|
||||||
super.interaction.setDefault();
|
super.interaction.setDefault();
|
||||||
this.dropper = player;
|
this.dropper = player;
|
||||||
|
this.dropperUid = player != null ? player.getDetails().getUid() : -1;
|
||||||
this.ticks = GameWorld.getTicks();
|
this.ticks = GameWorld.getTicks();
|
||||||
this.decayTime = ticks + decay;
|
this.decayTime = ticks + decay;
|
||||||
}
|
}
|
||||||
@@ -104,7 +113,7 @@ public class GroundItem extends Item {
|
|||||||
* @return {@code True} if so.
|
* @return {@code True} if so.
|
||||||
*/
|
*/
|
||||||
public boolean droppedBy(Player p) {
|
public boolean droppedBy(Player p) {
|
||||||
if (dropper != null && p.getDetails().getUid() == dropper.getDetails().getUid()) {
|
if (p.getDetails().getUid() == dropperUid) {
|
||||||
dropper = p;
|
dropper = p;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -191,6 +200,9 @@ public class GroundItem extends Item {
|
|||||||
this.removed = removed;
|
this.removed = removed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getDropperUid() {
|
||||||
|
return dropperUid;
|
||||||
|
}
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "GroundItem [dropper=" + (dropper != null ? dropper.getUsername() : dropper) + ", ticks=" + ticks + ", decayTime=" + decayTime + ", remainPrivate=" + remainPrivate + ", removed=" + removed + "]";
|
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));
|
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.
|
* Creates a ground item.
|
||||||
* @param item the item.
|
* @param item the item.
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import api.ShutdownListener;
|
|||||||
import core.game.node.entity.player.Player;
|
import core.game.node.entity.player.Player;
|
||||||
import rs09.Server;
|
import rs09.Server;
|
||||||
import rs09.ServerConstants;
|
import rs09.ServerConstants;
|
||||||
|
import rs09.ServerStore;
|
||||||
import rs09.game.ai.AIRepository;
|
import rs09.game.ai.AIRepository;
|
||||||
import rs09.game.system.SystemLogger;
|
import rs09.game.system.SystemLogger;
|
||||||
import rs09.game.world.GameWorld;
|
import rs09.game.world.GameWorld;
|
||||||
@@ -54,7 +55,16 @@ public final class SystemTermination {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
GameWorld.getShutdownListeners().forEach(ShutdownListener::shutdown);
|
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)
|
if(ServerConstants.DATA_PATH != null)
|
||||||
save(ServerConstants.DATA_PATH);
|
save(ServerConstants.DATA_PATH);
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
|
|||||||
@@ -442,6 +442,16 @@ public final class Location extends Node {
|
|||||||
return "[" + x + ", " + y + ", " + z + "]";
|
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
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return z << 30 | x << 15 | y;
|
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.
|
* @param node the Node to register a hint icon for.
|
||||||
*/
|
*/
|
||||||
fun registerHintIcon(player: Player, node: Node) {
|
fun registerHintIcon(player: Player, node: Node) {
|
||||||
|
if (getAttribute(player, "hinticon", null) != null)
|
||||||
|
return
|
||||||
setAttribute(player, "hinticon", HintIconManager.registerHintIcon(player, node))
|
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
|
package rs09.game.node.entity.player.info.login
|
||||||
|
|
||||||
import api.PersistPlayer
|
import api.PersistPlayer
|
||||||
import core.game.interaction.item.brawling_gloves.BrawlingGloves
|
|
||||||
import core.game.node.entity.combat.CombatSpell
|
import core.game.node.entity.combat.CombatSpell
|
||||||
import core.game.node.entity.player.Player
|
import core.game.node.entity.player.Player
|
||||||
import core.game.node.entity.player.link.IronmanMode
|
import core.game.node.entity.player.link.IronmanMode
|
||||||
import core.game.node.entity.player.link.SpellBookManager
|
import core.game.node.entity.player.link.SpellBookManager
|
||||||
import core.game.node.entity.player.link.emote.Emotes
|
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 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.JSONArray
|
||||||
import org.json.simple.JSONObject
|
import org.json.simple.JSONObject
|
||||||
import org.json.simple.parser.JSONParser
|
import org.json.simple.parser.JSONParser
|
||||||
import rs09.ServerConstants
|
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.CompostBins
|
||||||
import rs09.game.node.entity.skill.farming.FarmingPatch
|
import rs09.game.node.entity.skill.farming.FarmingPatch
|
||||||
import rs09.game.system.SystemLogger
|
import rs09.game.system.SystemLogger
|
||||||
@@ -287,8 +285,9 @@ class PlayerSaveParser(val player: Player) {
|
|||||||
|
|
||||||
fun parseGrave() {
|
fun parseGrave() {
|
||||||
saveFile ?: return
|
saveFile ?: return
|
||||||
val graveData = (saveFile!!["grave_type"] as String).toInt()
|
val graveData = (saveFile!!["grave_type"] as? String)?.toInt() ?: return
|
||||||
player.graveManager.type = GraveType.values()[graveData]
|
val type = GraveType.values()[graveData]
|
||||||
|
GraveController.updateGraveType(player, type)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun parseAppearance() {
|
fun parseAppearance() {
|
||||||
|
|||||||
@@ -39,7 +39,6 @@ class PlayerSaver (val player: Player){
|
|||||||
saveAppearance(saveFile)
|
saveAppearance(saveFile)
|
||||||
saveSpellbook(saveFile)
|
saveSpellbook(saveFile)
|
||||||
saveVarps(saveFile)
|
saveVarps(saveFile)
|
||||||
saveGraveType(saveFile)
|
|
||||||
saveSavedData(saveFile)
|
saveSavedData(saveFile)
|
||||||
saveAutocast(saveFile)
|
saveAutocast(saveFile)
|
||||||
saveConfigs(saveFile)
|
saveConfigs(saveFile)
|
||||||
@@ -517,10 +516,6 @@ class PlayerSaver (val player: Player){
|
|||||||
root.put("activityData",activityData)
|
root.put("activityData",activityData)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun saveGraveType(root: JSONObject){
|
|
||||||
root.put("grave_type",player.graveManager.type.ordinal.toString())
|
|
||||||
}
|
|
||||||
|
|
||||||
fun saveSpellbook(root: JSONObject){
|
fun saveSpellbook(root: JSONObject){
|
||||||
root.put("spellbook",player.spellBookManager.spellBook.toString())
|
root.put("spellbook",player.spellBookManager.spellBook.toString())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import core.game.world.map.RegionManager
|
|||||||
import core.plugin.CorePluginTypes.StartupPlugin
|
import core.plugin.CorePluginTypes.StartupPlugin
|
||||||
import core.tools.RandomFunction
|
import core.tools.RandomFunction
|
||||||
import rs09.ServerConstants
|
import rs09.ServerConstants
|
||||||
|
import rs09.ServerStore
|
||||||
import rs09.auth.AuthProvider
|
import rs09.auth.AuthProvider
|
||||||
import rs09.game.system.Auth
|
import rs09.game.system.Auth
|
||||||
import rs09.game.system.SystemLogger
|
import rs09.game.system.SystemLogger
|
||||||
@@ -169,7 +170,9 @@ object GameWorld {
|
|||||||
ConfigParser().prePlugin()
|
ConfigParser().prePlugin()
|
||||||
ClassScanner.scanClasspath()
|
ClassScanner.scanClasspath()
|
||||||
ClassScanner.loadPureInterfaces()
|
ClassScanner.loadPureInterfaces()
|
||||||
worldPersists.forEach { it.parse() }
|
val s = worldPersists.filterIsInstance<ServerStore>().first()
|
||||||
|
s.parse()
|
||||||
|
worldPersists.filter { it !is ServerStore }.forEach { it.parse() }
|
||||||
ClassScanner.loadSideEffectfulPlugins()
|
ClassScanner.loadSideEffectfulPlugins()
|
||||||
configParser.postPlugin()
|
configParser.postPlugin()
|
||||||
startupListeners.forEach { it.startup() }
|
startupListeners.forEach { it.startup() }
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ class PulseRunner {
|
|||||||
fun updateAll() {
|
fun updateAll() {
|
||||||
val pulseCount = pulses.size
|
val pulseCount = pulses.size
|
||||||
|
|
||||||
for (i in 0..pulseCount) {
|
for (i in 0 until pulseCount) {
|
||||||
val pulse = pulses.take()
|
val pulse = pulses.take()
|
||||||
|
|
||||||
val elapsedTime = measure {
|
val elapsedTime = measure {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import core.cache.crypto.ISAACCipher
|
|||||||
import core.cache.crypto.ISAACPair
|
import core.cache.crypto.ISAACPair
|
||||||
import core.game.node.entity.player.Player
|
import core.game.node.entity.player.Player
|
||||||
import core.game.node.entity.player.info.PlayerDetails
|
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.entity.player.link.IronmanMode
|
||||||
import core.game.node.item.Item
|
import core.game.node.item.Item
|
||||||
import core.net.IoSession
|
import core.net.IoSession
|
||||||
@@ -20,12 +21,16 @@ import rs09.game.world.update.UpdateSequence
|
|||||||
import java.nio.ByteBuffer
|
import java.nio.ByteBuffer
|
||||||
|
|
||||||
object TestUtils {
|
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)
|
val p = MockPlayer(name)
|
||||||
p.ironmanManager.mode = ironman
|
p.ironmanManager.mode = ironman
|
||||||
|
p.details.accountInfo.uid = uidCounter++
|
||||||
Repository.addPlayer(p)
|
Repository.addPlayer(p)
|
||||||
//Update sequence has a separate list of players for some reason...
|
//Update sequence has a separate list of players for some reason...
|
||||||
UpdateSequence.renderablePlayers.add(p)
|
UpdateSequence.renderablePlayers.add(p)
|
||||||
|
p.details.rights = rights
|
||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user