Public Repository for the Magicbane Shadowbane Emulator
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

401 lines
12 KiB

// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ .
// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌·
// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀
// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌
// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀
// 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();
this.setLoc(belongsTo.getLoc());
} 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.charItemManager;
if (cim != null)
cim.transferEntireInventory(this.inventory, this, enterWorld);
else
Logger.error("Can't find inventory for player " + belongsTo.getObjectUUID());
}
public Item lootItem(Item item, PlayerCharacter looter) {
//make sure looter exists
if (looter == null)
return null;
//get looters item manager
CharacterItemManager looterItems = looter.charItemManager;
if (looterItems == null)
return null;
synchronized (this.inventory) {
//make sure player has item in inventory
if (!this.inventory.contains(item))
return null;
//get weight of item
int weight = item.template.item_wt;
//make sure looter has room for item
if (item.template.item_type.equals(ItemType.GOLD) == false && !looterItems.hasRoomInventory(weight))
return null;
//attempt to transfer item in db
if (item.template.item_type.equals(ItemType.GOLD)) {
if (!looterItems.moveGoldToInventory(item, item.getNumOfItems()))
return null;
} else if (!item.moveItemToInventory(looter))
return null;
//db transfer successful, remove from this character
this.inventory.remove(this.inventory.indexOf(item));
}
//add item to looter.
if (!looterItems.addItemToInventory(item))
return null;
//calculate new weights
looterItems.calculateInventoryWeight();
return item;
}
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;
}
}