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.

703 lines
24 KiB

// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ .
// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌·
// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀
// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌
// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀
// Magicbane Emulator Project © 2013 - 2022
// www.magicbane.com
package engine.objects;
import engine.Enum;
import engine.InterestManagement.WorldGrid;
import engine.db.archive.DataWarehouse;
import engine.db.archive.MineRecord;
import engine.gameManager.*;
import engine.net.ByteBufferWriter;
import engine.net.DispatchMessage;
import engine.net.client.msg.ErrorPopupMsg;
import engine.net.client.msg.chat.ChatSystemMsg;
import engine.server.MBServerStatics;
import engine.workthreads.ZergMechanicThread;
import org.joda.time.DateTime;
import org.pmw.tinylog.Logger;
import java.net.UnknownHostException;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Time;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ThreadLocalRandom;
import static engine.gameManager.DbManager.MineQueries;
import static engine.gameManager.DbManager.getObject;
import static engine.math.FastMath.sqr;
public class Mine extends AbstractGameObject {
public static ConcurrentHashMap<Mine, Integer> mineMap = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_LOW);
public static ConcurrentHashMap<Integer, Mine> towerMap = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_LOW);
12 months ago
private String zoneName;
private Zone parentZone;
public boolean isActive = false;
public PlayerCharacter lastClaimer;
public boolean wasClaimed = false;
// Not persisted to DB
public String guildName;
public GuildTag guildTag;
public String nationName;
public GuildTag nationTag;
private Resource production;
private Guild owningGuild;
private int flags;
private int buildingID;
private MineProduction mineType;
public int capSize;
public final HashSet<Integer> _playerMemory = new HashSet<>();
public final HashMap<Integer,Long> _recentMemory = new HashMap<>();
public HashMap<Guild,ArrayList<Integer>> dividedPlayers;
1 year ago
public boolean hasProduced = false;
public static ArrayList<Mine> ChinaMines = new ArrayList<>();
public static ArrayList<Mine> EuroMines = new ArrayList<>();
public static ArrayList<Mine> AmericaMines = new ArrayList<>();
12 months ago
public boolean wasOpened = false;
public int liveHour;
public int liveMinute;
/**
* ResultSet Constructor
*/
public Mine(ResultSet rs) throws SQLException, UnknownHostException {
super(rs);
this.mineType = MineProduction.getByName(rs.getString("mine_type"));
//this.mineType = MineProduction.LUMBER;
int ownerUID = rs.getInt("mine_ownerUID");
this.buildingID = rs.getInt("mine_buildingUID");
this.flags = rs.getInt("flags");
12 months ago
//int parent = rs.getInt("parent");
11 months ago
if(BuildingManager.getBuildingFromCache(rs.getInt("mine_buildingUID")) != null) {
Building tower = BuildingManager.getBuildingFromCache(rs.getInt("mine_buildingUID"));
for (Zone zone : ZoneManager.getAllZonesIn(tower.loc)) {
if (zone.isMacroZone()) {
this.parentZone = zone;
this.zoneName = this.parentZone.getName();
11 months ago
}
12 months ago
}
11 months ago
this.owningGuild = Guild.getGuild(ownerUID);
Guild nation = null;
if (this.owningGuild.isEmptyGuild()) {
this.guildName = "";
this.guildTag = GuildTag.ERRANT;
nation = Guild.getErrantGuild();
this.owningGuild = Guild.getErrantGuild();
} else {
this.guildName = this.owningGuild.getName();
this.guildTag = this.owningGuild.getGuildTag();
nation = this.owningGuild.getNation();
}
11 months ago
if (!nation.isEmptyGuild()) {
this.nationName = nation.getName();
this.nationTag = nation.getGuildTag();
} else {
this.nationName = "";
this.nationTag = GuildTag.ERRANT;
}
11 months ago
this.production = Resource.valueOf(rs.getString("mine_resource"));
this.lastClaimer = null;
11 months ago
this.liveHour = rs.getInt("mineLiveHour");
this.liveMinute = rs.getInt("mineLiveMinute");
11 months ago
this.capSize = rs.getInt("capSize");
}
}
public static void releaseMineClaims(PlayerCharacter playerCharacter) {
if (playerCharacter == null)
return;
for (Mine mine : Mine.getMines()) {
if (mine.lastClaimer != null)
if (mine.lastClaimer.equals(playerCharacter)) {
mine.lastClaimer = null;
mine.updateGuildOwner(null);
}
}
}
public static void SendMineAttackMessage(Building mine) {
if (mine.getBlueprint() == null)
return;
if (mine.getBlueprint().getBuildingGroup() != Enum.BuildingGroup.MINE)
return;
if (mine.getGuild().isEmptyGuild())
return;
if (mine.getGuild().getNation().isEmptyGuild())
return;
if (mine.getTimeStamp("MineAttack") > System.currentTimeMillis())
return;
mine.getTimestamps().put("MineAttack", System.currentTimeMillis() + MBServerStatics.ONE_MINUTE);
ChatManager.chatNationInfo(mine.getGuild().getNation(), mine.getName() + " in " + mine.getParentZone().getParent().getName() + " is Under attack!");
}
public static void loadAllMines() {
//Load mine resources
MineProduction.addResources();
//pre-load all building sets
ArrayList<Mine> serverMines = MineQueries.GET_ALL_MINES_FOR_SERVER();
for (Mine mine : serverMines) {
Mine.mineMap.put(mine, mine.buildingID);
Mine.towerMap.put(mine.buildingID, mine);
}
}
/*
* Getters
*/
public static Mine getMineFromTower(int towerID) {
return Mine.towerMap.get(towerID);
}
public static void serializeForClientMsg(Mine mine, ByteBufferWriter writer) {
writer.putInt(mine.getObjectType().ordinal());
writer.putInt(mine.getObjectUUID());
writer.putInt(mine.getObjectUUID()); //actually a hash of mine
writer.putString(mine.mineType.name);
writer.putString(mine.capSize + " Man ");
writer.putInt(mine.production.hash);
1 year ago
writer.putInt(mine.getModifiedProductionAmount());
writer.putInt(mine.getModifiedProductionAmount()); //TODO calculate range penalty here
writer.putInt(3600); //window in seconds //production per hour in seconds?
// Errant mines are currently open. Set time to now.
LocalDateTime mineOpenTime = LocalDateTime.now().withHour(mine.liveHour).withMinute(mine.liveMinute).withSecond(0).withNano(0);
LocalDateTime mineCloseTime = mineOpenTime.plusMinutes(30);
if(LocalDateTime.now().isAfter(mineCloseTime)){
mineOpenTime = mineOpenTime.plusDays(1);
mineCloseTime = mineCloseTime.plusDays(1);
}
writer.putLocalDateTime(mineOpenTime);
writer.putLocalDateTime(mineCloseTime);
writer.put(mine.isActive ? (byte) 0x01 : (byte) 0x00);
Building mineTower = BuildingManager.getBuilding(mine.buildingID);
writer.putFloat(mineTower.getLoc().x);
writer.putFloat(mineTower.getParentZone().getLoc().y);
writer.putFloat(mineTower.getLoc().z);
writer.putInt(mine.isExpansion() ? mine.mineType.xpacHash : mine.mineType.hash);
writer.putString(mine.guildName);
GuildTag._serializeForDisplay(mine.guildTag, writer);
writer.putString(mine.nationName);
GuildTag._serializeForDisplay(mine.nationTag, writer);
}
public static ArrayList<Mine> getMinesForGuild(int guildID) {
ArrayList<Mine> mineList = new ArrayList<>();
// Only inactive mines are returned.
for (Mine mine : Mine.mineMap.keySet()) {
if (mine.owningGuild.getObjectUUID() == guildID &&
mine.isActive == false)
mineList.add(mine);
}
return mineList;
}
/*
* Database
*/
public static Mine getMine(int UID) {
return MineQueries.GET_MINE(UID);
}
public static ArrayList<Mine> getMines() {
return new ArrayList<>(mineMap.keySet());
}
public static boolean validateClaimer(PlayerCharacter playerCharacter) {
// Method validates that the claimer meets
// all the requirements to claim; landed
// guild with a warehouse, etc.
Guild playerGuild;
//verify the player exists
if (playerCharacter == null)
return false;
//verify the player is in valid guild
playerGuild = playerCharacter.getGuild();
2 years ago
// Can't claim something if you don't have a guild!
if (playerGuild.isEmptyGuild())
return false;
if (playerGuild.getNation().isEmptyGuild())
return false;
// Guild must own a city to hold a mine.
2 years ago
City guildCity = playerGuild.getOwnedCity();
if (guildCity == null)
return false;
if (guildCity.getWarehouse() == null) {
ErrorPopupMsg.sendErrorMsg(playerCharacter, "No Warehouse exists for this claim.");
return false;
}
// Number of mines is based on the rank of the nation's tree.
City nationCapitol = playerGuild.getNation().getOwnedCity();
Building nationCapitolTOL = nationCapitol.getTOL();
if (nationCapitolTOL == null)
return false;
//int treeRank = nationCapitolTOL.getRank();
//if (treeRank < 1)
// return false;
//if (guildUnderMineLimit(playerGuild.getNation(), treeRank) == false) {
// ErrorPopupMsg.sendErrorMsg(playerCharacter, "Your nation cannot support another mine.");
// return false;
//}
return true;
}
private static boolean guildUnderMineLimit(Guild playerGuild, int tolRank) {
int mineCnt = 0;
mineCnt += Mine.getMinesForGuild(playerGuild.getObjectUUID()).size();
for (Guild guild : playerGuild.getSubGuildList())
mineCnt += Mine.getMinesForGuild(guild.getObjectUUID()).size();
return mineCnt <= tolRank;
}
public boolean changeProductionType(Resource resource) {
1 year ago
//if (!this.validForMine(resource))
// return false;
//update resource in database;
if (!MineQueries.CHANGE_RESOURCE(this, resource))
return false;
1 year ago
if(this.isActive || this.hasProduced)
1 year ago
return false;
this.production = resource;
1 year ago
this.hasProduced = true;
ItemBase resourceIB = ItemBase.getItemBase(this.production.UUID);
1 year ago
1 year ago
return this.owningGuild.getOwnedCity().getWarehouse().depositFromMine(this, resourceIB, this.getModifiedProductionAmount());
}
public MineProduction getMineType() {
return this.mineType;
}
public void setMineType(String type) {
this.mineType = MineProduction.getByName(type);
}
public String getZoneName() {
return this.zoneName;
}
public Resource getProduction() {
return this.production;
}
public boolean getIsActive() {
return this.isActive;
}
public Guild getOwningGuild() {
if (this.owningGuild == null)
return Guild.getErrantGuild();
else
return this.owningGuild;
}
public void setOwningGuild(Guild owningGuild) {
this.owningGuild = owningGuild;
}
/*
* Serialization
*/
public int getFlags() {
return flags;
}
public void setFlags(int flags) {
this.flags = flags;
}
public Zone getParentZone() {
return parentZone;
}
public GuildTag getGuildTag() {
return guildTag;
}
public void setActive(boolean isAc) {
this.isActive = isAc;
Building building = BuildingManager.getBuildingFromCache(this.buildingID);
if (building != null && !this.isActive)
building.isDeranking.compareAndSet(true, false);
if(isAc) {
12 months ago
ZergMechanicThread.startZergThreadMine(this);
}
12 months ago
this.wasOpened = true;
}
public boolean validForMine(Resource r) {
if (this.mineType == null)
return false;
return this.mineType.validForMine(r, this.isExpansion());
}
public void serializeForMineProduction(ByteBufferWriter writer) {
writer.putInt(this.getObjectType().ordinal());
writer.putInt(this.getObjectUUID());
writer.putInt(this.getObjectUUID()); //actually a hash of mine
// writer.putInt(0x215C92BB); //this.unknown1);
writer.putString(this.mineType.name);
writer.putString(this.zoneName);
writer.putInt(this.production.hash);
writer.putInt(this.production.baseProduction);
writer.putInt(this.getModifiedProductionAmount()); //TODO calculate range penalty here
writer.putInt(3600); //window in seconds
writer.putInt(this.isExpansion() ? this.mineType.xpacHash : this.mineType.hash);
}
@Override
public void updateDatabase() {
// TODO Create update logic.
}
public int getBuildingID() {
return buildingID;
}
public void setBuildingID(int buildingID) {
this.buildingID = buildingID;
}
public void handleDestroyMine() {
if (!this.isActive)
return;
//remove tags from mine
this.guildName = "";
this.nationName = "";
this.owningGuild = Guild.getErrantGuild();
this.lastClaimer = null;
this.wasClaimed = false;
1 year ago
this.hasProduced = false;
// Update database
DbManager.MineQueries.CHANGE_OWNER(this, 0);
// Update mesh
Building mineBuilding = BuildingManager.getBuildingFromCache(this.buildingID);
if (mineBuilding == null) {
Logger.debug("Null mine building " + this.getObjectUUID() + ". Unable to Load Building with UID " + this.buildingID);
return;
}
mineBuilding.setOwner(null);
mineBuilding.refresh(false);
// remove hirelings
Building building = (Building) getObject(Enum.GameObjectType.Building, this.buildingID);
BuildingManager.cleanupHirelings(building);
}
public boolean claimMine(PlayerCharacter claimer) {
if (claimer == null)
return false;
if (!validateClaimer(claimer))
return false;
if (!this.isActive) {
ErrorPopupMsg.sendErrorMsg(claimer, "Can not for to claim inactive mine.");
return false;
}
if (!updateGuildOwner(claimer))
return false;
// Successful claim
this.lastClaimer = claimer;
return true;
}
public boolean depositMineResources() {
if (this.owningGuild.isEmptyGuild())
return false;
if (this.owningGuild.getOwnedCity() == null)
return false;
if (this.owningGuild.getOwnedCity().getWarehouse() == null)
return false;
ItemBase resourceIB = ItemBase.getItemBase(this.production.UUID);
return this.owningGuild.getOwnedCity().getWarehouse().depositFromMine(this, resourceIB, this.getModifiedProductionAmount());
}
public boolean updateGuildOwner(PlayerCharacter playerCharacter) {
Building mineBuilding = BuildingManager.getBuildingFromCache(this.buildingID);
//should never return null, but let's check just in case.
if (mineBuilding == null) {
ChatManager.chatSystemError(playerCharacter, "Unable to find mine tower.");
Logger.debug("Failed to Update Mine with UID " + this.getObjectUUID() + ". Unable to Load Building with UID " + this.buildingID);
return false;
}
if (playerCharacter == null) {
this.owningGuild = Guild.getErrantGuild();
this.guildName = "None";
this.guildTag = GuildTag.ERRANT;
this.nationName = "None";
this.nationTag = GuildTag.ERRANT;
//Update Building.
mineBuilding.setOwner(null);
WorldGrid.updateObject(mineBuilding);
return true;
}
Guild guild = playerCharacter.getGuild();
if (guild.getOwnedCity() == null)
return false;
if (!MineQueries.CHANGE_OWNER(this, guild.getObjectUUID())) {
Logger.debug("Database failed to Change Ownership of Mine with UID " + this.getObjectUUID());
ChatManager.chatSystemError(playerCharacter, "Failed to claim Mine.");
return false;
}
//update mine.
this.owningGuild = guild;
//Update Building.
PlayerCharacter guildLeader = (PlayerCharacter) Guild.GetGL(this.owningGuild);
if (guildLeader != null)
mineBuilding.setOwner(guildLeader);
WorldGrid.updateObject(mineBuilding);
return true;
}
public boolean isExpansion() {
1 year ago
return true;
}
public int getModifiedProductionAmount() {
1 year ago
int value = Warehouse.getCostForResource(this.production.UUID);
int amount = 0;
switch(this.capSize){
case 3:
amount = 1800000;
break;
1 year ago
case 5:
amount = 3000000;
break;
case 10:
amount = 6000000;
break;
case 20:
amount = 12000000;
1 year ago
break;
}
if(this.production.UUID == 7)
amount *= 0.5f;
else
1 year ago
amount = amount / value;
return (int) amount;
}
public Boolean onExit(HashSet<AbstractWorldObject> currentPlayers){
1 year ago
ArrayList<Integer> purge = new ArrayList<>();
for(int id : this._playerMemory){
PlayerCharacter player = PlayerCharacter.getPlayerCharacter(id);
if(!currentPlayers.contains(player)){
1 year ago
purge.add(id);
player.ZergMultiplier = 1.0f;
1 year ago
//ChatManager.chatSystemInfo(player,"Left Mine, Multiplier: " + player.ZergMultiplier);
1 year ago
}
}
1 year ago
1 year ago
if(purge.size() > 0) {
1 year ago
//this._playerMemory.removeAll(purge);
for(int id : purge){
if(!this._recentMemory.containsKey(id)) {
1 year ago
this._recentMemory.put(id, System.currentTimeMillis() + 60000);
} else if(this._recentMemory.get(id) > System.currentTimeMillis()){
this._playerMemory.remove(id);
1 year ago
//ChatManager.chatSystemInfo(PlayerCharacter.getPlayerCharacter(id),"Left Have Been Removed from The Mine List");
1 year ago
}
}
1 year ago
}
1 year ago
return true;
1 year ago
}
public static void mineWindowOpen(Mine mine) {
mine.setActive(true);
Logger.info(mine.getParentZone().getName() + "'s Mine is now Active!");
}
public static boolean mineWindowClose(Mine mine) {
// No need to end the window of a mine which never opened.
if (mine.isActive == false)
return false;
Building mineBuilding = BuildingManager.getBuildingFromCache(mine.getBuildingID());
if (mineBuilding == null) {
Logger.debug("Null mine building for Mine " + mine.getObjectUUID() + " Building " + mine.getBuildingID());
return false;
}
for(Integer id : mine._playerMemory ){
PlayerCharacter.getPlayerCharacter(id).ZergMultiplier = 1.0f;
}
for(Integer id : mine._recentMemory.keySet()){
PlayerCharacter.getPlayerCharacter(id).ZergMultiplier = 1.0f;
}
// Mine building still stands; nothing to do.
// We can early exit here.
if (mineBuilding.getRank() > 0) {
mine.setActive(false);
mine.lastClaimer = null;
return true;
}
// This mine does not have a valid claimer
// we will therefore set it to errant
// and keep the window open.
//if (!Mine.validateClaimer(mine.lastClaimer)) {
// mine.lastClaimer = null;
// mine.updateGuildOwner(null);
// mine.setActive(true);
// return false;
//}
//Update ownership to map
mine.guildName = mine.getOwningGuild().getName();
mine.guildTag = mine.getOwningGuild().getGuildTag();
Guild nation = mine.getOwningGuild().getNation();
mine.nationName = nation.getName();
mine.nationTag = nation.getGuildTag();
mineBuilding.rebuildMine(mine.capSize * 5000);
WorldGrid.updateObject(mineBuilding);
ChatSystemMsg chatMsg = new ChatSystemMsg(null, mine.lastClaimer.getName() + " has claimed the mine in " + mine.getParentZone().getName() + " for " + mine.getOwningGuild().getName() + ". The mine is no longer active.");
chatMsg.setMessageType(10);
chatMsg.setChannel(Enum.ChatChannelType.SYSTEM.getChannelID());
DispatchMessage.dispatchMsgToAll(chatMsg);
// Warehouse this claim event
MineRecord mineRecord = MineRecord.borrow(mine, mine.lastClaimer, Enum.RecordEventType.CAPTURE);
DataWarehouse.pushToWarehouse(mineRecord);
mineBuilding.setRank(mineBuilding.getRank());
mine.lastClaimer = null;
mine.setActive(false);
mine.wasClaimed = true;
return true;
}
public static void processMineWindows() {
LocalDateTime currentTime = LocalDateTime.now();
for (Mine mine : Mine.getMines()) {
try {
Building tower = BuildingManager.getBuildingFromCache(mine.getBuildingID());
//if the tower comes back null, skip this mine
if (tower == null)
continue;
//check if this mine needs to open
LocalDateTime openTime = LocalDateTime.now().withHour(mine.liveHour).withMinute(mine.liveMinute).withSecond(0).withNano(0);
if (currentTime.isAfter(openTime) && currentTime.isBefore(openTime.plusMinutes(30)) && !mine.wasOpened) {
mineWindowOpen(mine); //hour and minute match, time to open the window
ChatManager.chatSystemChannel(mine.getParentZone().getName() + " " + mine.getMineType() + "MINE is now vulnerable to attack!");
continue;
}
//check to see if the window shoul dbe closing now
if (currentTime.isAfter(openTime.plusMinutes(29)) && mine.isActive) {
//check to see if the tower was destoryed
boolean towerDestroyed = tower.getRank() < 1;
if (towerDestroyed) {
//check if a valid claimer exists to close the window and claim the mine since the tower was destroyed
if (mine.lastClaimer != null) {
mineWindowClose(mine);
ChatManager.chatSystemChannel("The fight for " + mine.getParentZone().getName() + " " + mine.getMineType() + " MINE has concluded. " + mine.lastClaimer.getName() + " has seized it in the name of " + mine.lastClaimer.getGuild().getNation());
} else {
ChatManager.chatSystemChannel("The " + mine.getParentZone().getName() + " " + mine.getMineType() + " MINE is still unclaimed. The battle continues.");
}
} else {
//tower was not destroyed, mine window closes
mineWindowClose(mine);
ChatManager.chatSystemChannel(tower.getGuild().getNation().getName() + " has successfully defended the " + mine.getParentZone().getName() + " " + mine.getMineType() + " MINE, and retains their claim.");
}
}
} catch (Exception ex) {
Logger.error("Mine Failed: " + mine.getObjectUUID() + " " + ex.getMessage());
}
}
}
}