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
401 lines
12 KiB
// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . |
|
// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· |
|
// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ |
|
// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ |
|
// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ |
|
// Magicbane Emulator Project © 2013 - 2022 |
|
// www.magicbane.com |
|
|
|
|
|
package engine.objects; |
|
|
|
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.mbEnums.GameObjectType; |
|
import engine.mbEnums.GridObjectType; |
|
import engine.mbEnums.ItemType; |
|
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; |
|
} |
|
}
|
|
|