|
|
|
// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ .
|
|
|
|
// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌·
|
|
|
|
// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀
|
|
|
|
// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌
|
|
|
|
// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀
|
|
|
|
// Magicbane Emulator Project © 2013 - 2022
|
|
|
|
// www.magicbane.com
|
|
|
|
|
|
|
|
|
|
|
|
package engine.objects;
|
|
|
|
|
|
|
|
import engine.Enum.GameObjectType;
|
|
|
|
import engine.Enum.GridObjectType;
|
|
|
|
import engine.Enum.ItemType;
|
|
|
|
import engine.InterestManagement.WorldGrid;
|
|
|
|
import engine.exception.SerializationException;
|
|
|
|
import engine.gameManager.BuildingManager;
|
|
|
|
import engine.gameManager.DbManager;
|
|
|
|
import engine.job.JobContainer;
|
|
|
|
import engine.job.JobScheduler;
|
|
|
|
import engine.jobs.RemoveCorpseJob;
|
|
|
|
import engine.net.ByteBufferWriter;
|
|
|
|
import engine.net.DispatchMessage;
|
|
|
|
import engine.net.client.msg.UnloadObjectsMsg;
|
|
|
|
import engine.server.MBServerStatics;
|
|
|
|
import org.pmw.tinylog.Logger;
|
|
|
|
|
|
|
|
import java.util.ArrayList;
|
|
|
|
import java.util.concurrent.atomic.AtomicInteger;
|
|
|
|
|
|
|
|
public class Corpse extends AbstractWorldObject {
|
|
|
|
|
|
|
|
private static AtomicInteger corpseCounter = new AtomicInteger(0);
|
|
|
|
public JobContainer cleanup;
|
|
|
|
private String firstName;
|
|
|
|
private String lastName;
|
|
|
|
private int level;
|
|
|
|
private int belongsToType;
|
|
|
|
private int belongsToID;
|
|
|
|
private ArrayList<Item> inventory;
|
|
|
|
private boolean asciiLastName = true;
|
|
|
|
private boolean hasGold = false;
|
|
|
|
private int inBuildingID = 0;
|
|
|
|
private int inFloorID = -1;
|
|
|
|
private int inBuilding = -1;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* No Id Constructor
|
|
|
|
*/
|
|
|
|
public Corpse(int newUUID, AbstractCharacter belongsTo, boolean safeZone, boolean enterWorld) {
|
|
|
|
super(newUUID);
|
|
|
|
this.setObjectType();
|
|
|
|
this.inventory = new ArrayList<>();
|
|
|
|
this.gridObjectType = GridObjectType.STATIC;
|
|
|
|
this.setObjectTypeMask(MBServerStatics.MASK_CORPSE);
|
|
|
|
if (belongsTo != null) {
|
|
|
|
this.firstName = belongsTo.getFirstName();
|
|
|
|
this.lastName = belongsTo.getLastName();
|
|
|
|
this.asciiLastName = belongsTo.asciiLastName();
|
|
|
|
this.level = belongsTo.getLevel();
|
|
|
|
this.belongsToType = belongsTo.getObjectType().ordinal();
|
|
|
|
this.belongsToID = belongsTo.getObjectUUID();
|
|
|
|
this.inBuilding = belongsTo.getInBuilding();
|
|
|
|
this.inFloorID = belongsTo.getInFloorID();
|
|
|
|
this.inBuildingID = belongsTo.getInBuildingID();
|
|
|
|
belongsTo.stopMovement(belongsTo.getMovementLoc());
|
|
|
|
this.setLoc(belongsTo.getMovementLoc());
|
|
|
|
} else {
|
|
|
|
Logger.error("No player passed in for corpse");
|
|
|
|
this.firstName = "";
|
|
|
|
this.lastName = "";
|
|
|
|
this.level = 1;
|
|
|
|
this.belongsToType = 0;
|
|
|
|
this.belongsToID = 0;
|
|
|
|
}
|
|
|
|
this.setObjectTypeMask(MBServerStatics.MASK_CORPSE);
|
|
|
|
|
|
|
|
if (!safeZone)
|
|
|
|
transferInventory(belongsTo, enterWorld);
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
public static int getNextCorpseCount() {
|
|
|
|
return Corpse.corpseCounter.addAndGet(2); //newUUID and runeID
|
|
|
|
}
|
|
|
|
|
|
|
|
//Create a new corpse
|
|
|
|
public static Corpse makeCorpse(AbstractCharacter belongsTo, boolean enterWorld) {
|
|
|
|
boolean safeZone = false;
|
|
|
|
if (belongsTo != null && belongsTo.getObjectType() == GameObjectType.PlayerCharacter)
|
|
|
|
safeZone = ((PlayerCharacter) belongsTo).isInSafeZone();
|
|
|
|
|
|
|
|
|
|
|
|
Corpse corpse = new Corpse(Corpse.getNextCorpseCount(), belongsTo, safeZone, enterWorld);
|
|
|
|
|
|
|
|
//create cleanup job
|
|
|
|
if (corpse != null) {
|
|
|
|
RemoveCorpseJob rcj = new RemoveCorpseJob(corpse);
|
|
|
|
corpse.cleanup = JobScheduler.getInstance().scheduleJob(rcj, MBServerStatics.CORPSE_CLEANUP_TIMER_MS);
|
|
|
|
DbManager.addToCache(corpse);
|
|
|
|
}
|
|
|
|
|
|
|
|
return corpse;
|
|
|
|
}
|
|
|
|
|
|
|
|
//Get existing corpse
|
|
|
|
public static Corpse getCorpse(int newUUID) {
|
|
|
|
return (Corpse) DbManager.getFromCache(GameObjectType.Corpse, newUUID);
|
|
|
|
}
|
|
|
|
|
|
|
|
//remove corpse from world
|
|
|
|
public static void removeCorpse(int newUUID, boolean fromTimer) {
|
|
|
|
Corpse c = (Corpse) DbManager.getFromCache(GameObjectType.Corpse, newUUID);
|
|
|
|
if (c == null)
|
|
|
|
Logger.error("No corpse found of ID " + newUUID);
|
|
|
|
else
|
|
|
|
Corpse.removeCorpse(c, fromTimer);
|
|
|
|
}
|
|
|
|
|
|
|
|
public static void removeCorpse(Corpse corpse, boolean fromTimer) {
|
|
|
|
if (corpse == null)
|
|
|
|
return;
|
|
|
|
|
|
|
|
corpse.purgeInventory();
|
|
|
|
|
|
|
|
//cleanup timer
|
|
|
|
if (!fromTimer) {
|
|
|
|
JobScheduler.getInstance().cancelScheduledJob(corpse.cleanup);
|
|
|
|
}
|
|
|
|
corpse.cleanup = null;
|
|
|
|
|
|
|
|
//Remove from world
|
|
|
|
UnloadObjectsMsg uom = new UnloadObjectsMsg();
|
|
|
|
uom.addObject(corpse);
|
|
|
|
DispatchMessage.sendToAllInRange(corpse, uom);
|
|
|
|
WorldGrid.RemoveWorldObject(corpse);
|
|
|
|
|
|
|
|
//clear from cache
|
|
|
|
DbManager.removeFromCache(corpse);
|
|
|
|
}
|
|
|
|
|
|
|
|
public static void _serializeForClientMsg(Corpse corpse, ByteBufferWriter writer)
|
|
|
|
throws SerializationException {
|
|
|
|
}
|
|
|
|
|
|
|
|
public static void _serializeForClientMsg(Corpse corpse, ByteBufferWriter writer, boolean aln)
|
|
|
|
throws SerializationException {
|
|
|
|
|
|
|
|
Building building = null;
|
|
|
|
if (corpse.inBuildingID != 0)
|
|
|
|
building = BuildingManager.getBuildingFromCache(corpse.inBuildingID);
|
|
|
|
|
|
|
|
//Send Rune Count
|
|
|
|
writer.putInt(0);
|
|
|
|
writer.putInt(0);
|
|
|
|
writer.putInt(1);
|
|
|
|
|
|
|
|
//Send Corpse Rune
|
|
|
|
writer.putInt(1);
|
|
|
|
writer.putInt(0);
|
|
|
|
writer.putInt(MBServerStatics.TOMBSTONE);
|
|
|
|
writer.putInt(corpse.getObjectType().ordinal());
|
|
|
|
writer.putInt((corpse.getObjectUUID() + 1));
|
|
|
|
|
|
|
|
//Send Stats
|
|
|
|
writer.putInt(5);
|
|
|
|
writer.putInt(MBServerStatics.STAT_STR_ID); // Strength ID
|
|
|
|
writer.putInt(5000);
|
|
|
|
writer.putInt(MBServerStatics.STAT_SPI_ID); // Spirit ID
|
|
|
|
writer.putInt(0);
|
|
|
|
writer.putInt(MBServerStatics.STAT_CON_ID); // Constitution ID
|
|
|
|
writer.putInt(0);
|
|
|
|
writer.putInt(MBServerStatics.STAT_DEX_ID); // Dexterity ID
|
|
|
|
writer.putInt(0);
|
|
|
|
writer.putInt(MBServerStatics.STAT_INT_ID); // Intelligence ID
|
|
|
|
writer.putInt(0);
|
|
|
|
|
|
|
|
//Send Name
|
|
|
|
writer.putString(corpse.firstName);
|
|
|
|
if (aln && !corpse.asciiLastName)
|
|
|
|
writer.putString("");
|
|
|
|
else
|
|
|
|
writer.putString(corpse.lastName);
|
|
|
|
writer.putInt(0);
|
|
|
|
writer.putInt(0);
|
|
|
|
writer.putInt(0);
|
|
|
|
writer.put((byte) 1);
|
|
|
|
|
|
|
|
//Send Corpse Info
|
|
|
|
writer.putInt(0);
|
|
|
|
writer.putInt(corpse.getObjectType().ordinal());
|
|
|
|
writer.putInt((corpse.getObjectUUID()));
|
|
|
|
writer.putFloat(10f); //FaceDir or scale
|
|
|
|
writer.putFloat(10); //FaceDir or scale
|
|
|
|
writer.putFloat(10); //FaceDir or scale
|
|
|
|
|
|
|
|
writer.putFloat(corpse.getLoc().x);
|
|
|
|
writer.putFloat(corpse.getLoc().y);
|
|
|
|
writer.putFloat(corpse.getLoc().z);
|
|
|
|
|
|
|
|
writer.putFloat(6.235f); //1.548146f); //w
|
|
|
|
writer.putInt(0);
|
|
|
|
writer.putInt(0);
|
|
|
|
|
|
|
|
//Send BelongsToInfo
|
|
|
|
writer.putInt(((corpse.level / 10))); //Rank
|
|
|
|
writer.putInt(corpse.level); //Level
|
|
|
|
writer.putInt(1);
|
|
|
|
writer.putInt(1);
|
|
|
|
writer.putInt(1); //Missing this?
|
|
|
|
writer.putInt(2);
|
|
|
|
writer.putInt(1);
|
|
|
|
// writer.putInt(0); //not needed?
|
|
|
|
writer.putInt(0);
|
|
|
|
|
|
|
|
writer.putInt(corpse.belongsToType);
|
|
|
|
writer.putInt(corpse.belongsToID);
|
|
|
|
|
|
|
|
writer.putInt(0);
|
|
|
|
writer.putInt(0);
|
|
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < 9; i++)
|
|
|
|
writer.putInt(0);
|
|
|
|
writer.putShort((short) 0);
|
|
|
|
writer.put((byte) 0);
|
|
|
|
|
|
|
|
//Send Errant Guild Info
|
|
|
|
for (int i = 0; i < 13; i++)
|
|
|
|
writer.putInt(0);
|
|
|
|
writer.putInt(16);
|
|
|
|
writer.putInt(16);
|
|
|
|
writer.putInt(16);
|
|
|
|
writer.putInt(0);
|
|
|
|
writer.putInt(0); //Missing this?
|
|
|
|
writer.putInt(16);
|
|
|
|
writer.putInt(16);
|
|
|
|
writer.putInt(16);
|
|
|
|
writer.putInt(0);
|
|
|
|
writer.putInt(0);
|
|
|
|
writer.putInt(0);
|
|
|
|
|
|
|
|
//Send unknown counter
|
|
|
|
writer.putInt(1);
|
|
|
|
writer.putInt(0x047A0E67); //What is this?
|
|
|
|
writer.put((byte) 0);
|
|
|
|
|
|
|
|
//Send unknown
|
|
|
|
writer.putInt(0);
|
|
|
|
writer.putInt(0);
|
|
|
|
writer.putFloat(1293.4449f); //Unknown
|
|
|
|
writer.putFloat(-100f); //Unknown
|
|
|
|
writer.putInt(0);
|
|
|
|
writer.put((byte) 0);
|
|
|
|
|
|
|
|
writer.put((byte) 0); //End datablock
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
public boolean removeItemFromInventory(Item item) {
|
|
|
|
synchronized (this.inventory) {
|
|
|
|
if (this.inventory.contains(item)) {
|
|
|
|
this.inventory.remove(item);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public void transferInventory(AbstractCharacter belongsTo, boolean enterWorld) {
|
|
|
|
if (belongsTo == null) {
|
|
|
|
Logger.error("Can't find player that corpse " + this.getObjectUUID() + " belongs to");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
//TODO transfer items from players inventory and trade window to corpse
|
|
|
|
CharacterItemManager cim = belongsTo.getCharItemManager();
|
|
|
|
if (cim != null)
|
|
|
|
cim.transferEntireInventory(this.inventory, this, enterWorld);
|
|
|
|
else
|
|
|
|
Logger.error("Can't find inventory for player " + belongsTo.getObjectUUID());
|
|
|
|
}
|
|
|
|
|
|
|
|
public Item lootItem(Item i, PlayerCharacter looter) {
|
|
|
|
//make sure looter exists
|
|
|
|
if (looter == null)
|
|
|
|
return null;
|
|
|
|
|
|
|
|
//get looters item manager
|
|
|
|
CharacterItemManager looterItems = looter.getCharItemManager();
|
|
|
|
if (looterItems == null)
|
|
|
|
return null;
|
|
|
|
|
|
|
|
synchronized (this.inventory) {
|
|
|
|
|
|
|
|
//make sure player has item in inventory
|
|
|
|
if (!this.inventory.contains(i))
|
|
|
|
return null;
|
|
|
|
|
|
|
|
//get weight of item
|
|
|
|
ItemBase ib = i.getItemBase();
|
|
|
|
if (ib == null)
|
|
|
|
return null;
|
|
|
|
short weight = ib.getWeight();
|
|
|
|
|
|
|
|
//make sure looter has room for item
|
|
|
|
if (ib.getType().equals(ItemType.GOLD) == false && !looterItems.hasRoomInventory(weight))
|
|
|
|
return null;
|
|
|
|
|
|
|
|
//attempt to transfer item in db
|
|
|
|
if (ib.getType().equals(ItemType.GOLD)) {
|
|
|
|
if (!looterItems.moveGoldToInventory(i, i.getNumOfItems()))
|
|
|
|
return null;
|
|
|
|
} else if (!i.moveItemToInventory(looter))
|
|
|
|
return null;
|
|
|
|
|
|
|
|
//db transfer successful, remove from this character
|
|
|
|
this.inventory.remove(this.inventory.indexOf(i));
|
|
|
|
}
|
|
|
|
|
|
|
|
//add item to looter.
|
|
|
|
if (!looterItems.addItemToInventory(i))
|
|
|
|
return null;
|
|
|
|
|
|
|
|
//calculate new weights
|
|
|
|
looterItems.calculateInventoryWeight();
|
|
|
|
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
|
|
|
public boolean hasGold() {
|
|
|
|
return this.hasGold;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void setHasGold(boolean value) {
|
|
|
|
this.hasGold = value;
|
|
|
|
}
|
|
|
|
|
|
|
|
public ArrayList<Item> getInventory() {
|
|
|
|
synchronized (this.inventory) {
|
|
|
|
return this.inventory;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Delete and remove all items in the inventory
|
|
|
|
*/
|
|
|
|
private void purgeInventory() {
|
|
|
|
//make a copy so we're not inside synchronized{} while waiting for all items to be junked
|
|
|
|
ArrayList<Item> inventoryCopy;
|
|
|
|
synchronized (this.inventory) {
|
|
|
|
inventoryCopy = new ArrayList<>(this.inventory);
|
|
|
|
this.inventory.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
for (Item item : inventoryCopy) {
|
|
|
|
item.junk();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void updateDatabase() {
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void runAfterLoad() {
|
|
|
|
}
|
|
|
|
|
|
|
|
public int getBelongsToType() {
|
|
|
|
return this.belongsToType;
|
|
|
|
}
|
|
|
|
|
|
|
|
public int getBelongsToID() {
|
|
|
|
return this.belongsToID;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public String getName() {
|
|
|
|
if (this.firstName.length() == 0) {
|
|
|
|
return "Unknown corpse";
|
|
|
|
}
|
|
|
|
if (this.lastName.length() == 0) {
|
|
|
|
return this.firstName;
|
|
|
|
}
|
|
|
|
return this.firstName + ' ' + this.lastName;
|
|
|
|
}
|
|
|
|
|
|
|
|
public int getInBuilding() {
|
|
|
|
return inBuilding;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void setInBuilding(int inBuilding) {
|
|
|
|
this.inBuilding = inBuilding;
|
|
|
|
}
|
|
|
|
|
|
|
|
public int getInFloorID() {
|
|
|
|
return inFloorID;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void setInFloorID(int inFloorID) {
|
|
|
|
this.inFloorID = inFloorID;
|
|
|
|
}
|
|
|
|
}
|