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.

628 lines
17 KiB

package engine.objects;
// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ .
// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌·
// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀
// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌
// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀
// Magicbane Emulator Project © 2013 - 2022
// www.magicbane.com
import engine.Enum.BuildingGroup;
import engine.gameManager.DbManager;
import engine.math.Vector2f;
import org.pmw.tinylog.Logger;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
/* @Summary - Blueprint class is used for determining
characteristics of instanced player owned
structures such as available slots, upgrade
cost/time and the target window symbol icon.
*/
public class Blueprint {
public final static Vector2f IrikieForgeExtents = new Vector2f(32, 32);
public final static Vector2f IrikieBarracksExtents = new Vector2f(32, 32);
public static HashMap<Integer, Blueprint> _meshLookup = new HashMap<>();
private static HashMap<Integer, Blueprint> _blueprints = new HashMap<>();
private static HashMap<Integer, Integer> _doorNumbers = new HashMap<>();
private final int blueprintUUID;
private final String name;
private final BuildingGroup buildingGroup;
private final int icon;
private final int maxRank;
private final int maxSlots;
private final int rank1UUID;
private final int rank3UUID;
private final int rank7UUID;
private final int destroyedUUID;
private Blueprint() {
this.blueprintUUID = 0;
this.name = "";
this.icon = 0;
this.buildingGroup = BuildingGroup.BANESTONE;
this.maxRank = 0;
this.maxSlots = 0;
this.rank1UUID = 0;
this.rank3UUID = 0;
this.rank7UUID = 0;
this.destroyedUUID = 0;
}
public Blueprint(ResultSet rs) throws SQLException {
this.blueprintUUID = rs.getInt("Rank0UUID");
this.name = rs.getString("MeshName");
this.icon = rs.getInt("Icon");
this.buildingGroup = BuildingGroup.valueOf(rs.getString("BuildingGroup"));
this.maxRank = rs.getInt("MaxRank");
this.maxSlots = rs.getInt("MaxSlots");
this.rank1UUID = rs.getInt("Rank1UUID");
this.rank3UUID = rs.getInt("Rank3UUID");
this.rank7UUID = rs.getInt("Rank7UUID");
this.destroyedUUID = rs.getInt("DestroyedUUID");
}
// Accessors
public static Blueprint getBlueprint(int blueprintUUID) {
return _blueprints.get(blueprintUUID);
}
public static BuildingGroup getBuildingGroup(int blueprintUUID) {
Blueprint blueprint;
blueprint = _blueprints.get(blueprintUUID);
return blueprint.buildingGroup;
}
public static int getMaxShrines(int treeRank) {
// Returns the number of allowed spires/shrines
// for a given rank.
int maxShrines;
switch (treeRank) {
case 0:
case 1:
case 2:
maxShrines = 0;
break;
case 3:
case 4:
maxShrines = 1;
break;
case 5:
case 6:
maxShrines = 2;
break;
case 7:
case 8:
maxShrines = 3;
break;
default:
maxShrines = 0;
}
return maxShrines;
}
public static void loadAllBlueprints() {
_blueprints = DbManager.BlueprintQueries.LOAD_ALL_BLUEPRINTS();
}
// Method returns a blueprint based on a blueprintUUID
public static void loadAllDoorNumbers() {
_doorNumbers = DbManager.BlueprintQueries.LOAD_ALL_DOOR_NUMBERS();
}
public static int getDoorNumberbyMesh(int doorMeshUUID) {
if (_doorNumbers.containsKey(doorMeshUUID))
return _doorNumbers.get(doorMeshUUID);
return 0;
}
public static boolean isMeshWallPiece(int meshUUID) {
Blueprint buildingBlueprint = Blueprint.getBlueprint(meshUUID);
if (buildingBlueprint == null)
return false;
switch (buildingBlueprint.buildingGroup) {
case WALLSTRAIGHT:
case ARTYTOWER:
case WALLCORNER:
case SMALLGATE:
case WALLSTAIRS:
case WALLSTRAIGHTTOWER:
return true;
default:
break;
}
return false;
}
// Method calculates available vendor slots
// based upon the building's current rank
public static int getNpcMaintCost(int rank) {
int maintCost = Integer.MAX_VALUE;
maintCost = (9730 * rank) + 1890;
return maintCost;
}
public int getMaxRank() {
return maxRank;
}
public int getMaxSlots() {
if (this.buildingGroup != null && this.buildingGroup.equals(BuildingGroup.BARRACK))
return 1;
return maxSlots;
}
// Method returns a mesh UUID for this blueprint
// based upon a given rank.
public BuildingGroup getBuildingGroup() {
return this.buildingGroup;
}
// Method returns a cost to upgrade a building to a given rank
// based upon this blueprint's maintenance group
public int getMaxHealth(int currentRank) {
int maxHealth;
// Return 0 health for a destroyed building
// or 1 for a destroyed mine (cleint looting restriction)
if (currentRank == -1) {
return this.buildingGroup == BuildingGroup.MINE ? 1 : 0;
}
// Return 15k for a constructing mesh
if (currentRank == 0)
return 15000;
switch (this.buildingGroup) {
case TOL:
maxHealth = (70000 * currentRank) + 10000;
break;
case BARRACK:
maxHealth = (35000 * currentRank) + 5000;
break;
case BANESTONE:
maxHealth = (170000 * currentRank) - 120000;
break;
case CHURCH:
maxHealth = (28000 * currentRank) + 4000;
break;
case MAGICSHOP:
case FORGE:
case INN:
case TAILOR:
maxHealth = (17500 * currentRank) + 2500;
break;
case VILLA:
case ESTATE:
case FORTRESS:
maxHealth = 300000;
break;
case CITADEL:
maxHealth = 500000;
break;
case SPIRE:
maxHealth = (37000 * currentRank) - 9000;
break;
case GENERICNOUPGRADE:
case SHACK:
case SIEGETENT:
maxHealth = 40000;
break;
case BULWARK:
if (currentRank == 1)
maxHealth = 110000;
else
maxHealth = 40000;
break;
case WALLSTRAIGHT:
case WALLSTRAIGHTTOWER:
case WALLSTAIRS:
maxHealth = 1000000;
break;
case WALLCORNER:
case ARTYTOWER:
maxHealth = 900000;
break;
case SMALLGATE:
maxHealth = 1100000;
break;
case AMAZONHALL:
case CATHEDRAL:
case GREATHALL:
case KEEP:
case THIEFHALL:
case TEMPLEHALL:
case WIZARDHALL:
case ELVENHALL:
case ELVENSANCTUM:
case IREKEIHALL:
case FORESTHALL:
maxHealth = (28000 * currentRank) + 4000;
break;
case MINE:
maxHealth = 125000;
break;
case RUNEGATE:
maxHealth = 100000;
break;
case SHRINE:
maxHealth = 100000;
break;
case WAREHOUSE:
maxHealth = 40000;
break;
default:
maxHealth = 40000;
break;
}
return maxHealth;
}
// Returns number of vendor slots available
// for the building's current rank.
public int getSlotsForRank(int currentRank) {
int availableSlots;
// Early exit for buildings not yet constructed
if (currentRank == 0)
return 0;
// Early exit for buildings with single or no slots
if (this.maxSlots <= 1)
return maxSlots;
if (this.maxRank == 1 && currentRank == 1)
return getMaxSlots();
switch (currentRank) {
case 1:
case 2:
availableSlots = 1;
break;
case 3:
case 4:
case 5:
case 6:
availableSlots = 2;
break;
case 7:
availableSlots = 3;
break;
case 8:
availableSlots = 1;
break;
default:
availableSlots = 0;
break;
}
return availableSlots;
}
// Returns the half extents of this blueprint's
// bounding box, based upon it's buildinggroup
public int getIcon() {
return this.icon;
}
public String getName() {
return this.name;
}
public int getMeshForRank(int targetRank) {
int targetMesh = this.blueprintUUID;
// The Blueprint UUID is the 'constructing' mesh so
// we return that value if the rank passed is 0.
if ((maxRank == 1) && (this.rank1UUID == 0)) {
return blueprintUUID;
}
// Set the return value to the proper mesh UID for rank
switch (targetRank) {
case -1:
targetMesh = this.destroyedUUID; // -1 Rank is a destroyed mesh
break;
case 0:
targetMesh = this.blueprintUUID; // Rank 0 is the 'constructing' mesh
break;
case 1:
case 2:
targetMesh = this.rank1UUID;
break;
case 3:
case 4:
case 5:
case 6:
targetMesh = this.rank3UUID;
break;
case 7:
case 8:
targetMesh = this.rank7UUID;
break;
default:
break;
}
return targetMesh;
}
public int getRankCost(int targetRank) {
// Set a MAXINT rankcost in case something goes wrong
int rankCost = Integer.MAX_VALUE;
// Sanity chack for retrieving a rankcost outside proper range
if ((targetRank > maxRank) || (targetRank < 0)) {
Logger.error("Attempt to retrieve rankcost for rank of" + targetRank);
return rankCost;
}
// Select linear equation for rank cost based upon the
// buildings current Maintenance BuildingGroup.
switch (this.buildingGroup) {
case GENERICNOUPGRADE:
case WALLSTRAIGHT:
case WALLSTAIRS:
case WALLCORNER:
case SMALLGATE:
case ARTYTOWER:
case SIEGETENT:
case BULWARK:
case BANESTONE:
case SHACK:
break; // This set cannot be upgraded. Returns max integer.
case TOL:
rankCost = (880000 * targetRank) - 440000;
break;
case BARRACK:
case VILLA:
case ESTATE:
case FORTRESS:
case CITADEL:
rankCost = (451000 * targetRank) - 308000;
break;
case CHURCH:
rankCost = (682000 * targetRank) - 110000;
break;
case FORGE:
case INN:
case TAILOR:
case MAGICSHOP:
rankCost = (440000 * targetRank) - 550000;
break;
case SPIRE:
rankCost = (176000 * targetRank) - 88000;
break;
case AMAZONHALL:
case CATHEDRAL:
case GREATHALL:
case KEEP:
case THIEFHALL:
case TEMPLEHALL:
case WIZARDHALL:
case ELVENHALL:
case ELVENSANCTUM:
case IREKEIHALL:
case FORESTHALL:
rankCost = (682000 * targetRank) - 110000;
break;
default:
Logger.error("Attempt to retrieve rankcost without MaintGroup for " + this.buildingGroup.name());
break;
}
return rankCost;
}
public int getRankTime(int targetRank) {
// Set a very long rankTime in case something goes wrong
int rankTime = (Integer.MAX_VALUE / 2);
// Set all initial construction to a default of 4 hours.
if (targetRank == 1)
return 4;
// Sanity chack for retrieving a ranktime outside proper range
if ((targetRank > maxRank) || (targetRank < 1)) {
Logger.error("Attempt to retrieve ranktime for rank of" + targetRank);
return rankTime;
}
// Select equation for rank time based upon the
// buildings current Maintenance BuildingGroup. These values
// are expressed in hours
switch (this.buildingGroup) {
case GENERICNOUPGRADE:
break; // Cannot be upgraded
case VILLA:
case ESTATE:
case FORTRESS:
case CITADEL:
rankTime = (7 * targetRank) - 7;
break;
case TOL:
rankTime = (7 * targetRank) - 7;
break;
case BARRACK:
rankTime = (7 * targetRank) - 7;
break;
case CHURCH:
rankTime = (7 * targetRank) - 7;
break;
case FORGE:
case INN:
case TAILOR:
case MAGICSHOP:
rankTime = (7 * targetRank) - 7;
break;
case SPIRE:
rankTime = (4 * targetRank) + 4;
break;
case AMAZONHALL:
case CATHEDRAL:
case GREATHALL:
case KEEP:
case THIEFHALL:
case TEMPLEHALL:
case WIZARDHALL:
case ELVENHALL:
case ELVENSANCTUM:
case IREKEIHALL:
case FORESTHALL:
rankTime = (7 * targetRank) - 7;
break;
default:
Logger.error("Attempt to retrieve ranktime without MaintGroup");
break;
}
return rankTime;
}
public Vector2f getExtents() {
if (blueprintUUID == 1302600)
return Blueprint.IrikieForgeExtents;
else if (blueprintUUID == 1300600)
return Blueprint.IrikieBarracksExtents;
return this.buildingGroup.getExtents();
}
public boolean isWallPiece() {
switch (this.buildingGroup) {
case WALLSTRAIGHT:
case WALLSTAIRS:
case ARTYTOWER:
case WALLCORNER:
case SMALLGATE:
return true;
default:
break;
}
return false;
}
public boolean isSiegeEquip() {
switch (this.buildingGroup) {
case BULWARK:
case SIEGETENT:
return true;
default:
break;
}
return false;
}
public int getBlueprintUUID() {
return blueprintUUID;
}
@Override
public boolean equals(Object object) {
if ((object instanceof Blueprint) == false)
return false;
Blueprint blueprint = (Blueprint) object;
return this.blueprintUUID == blueprint.blueprintUUID;
}
@Override
public int hashCode() {
return this.blueprintUUID;
}
public int getMaintCost(int rank) {
int maintCost = Integer.MAX_VALUE;
switch (this.buildingGroup) {
case TOL:
case BARRACK:
maintCost = (61500 * rank) + 19500;
break;
case SPIRE:
maintCost = (4800 * rank) + 1200;
break;
default:
if (maxRank == 1)
maintCost = 22500;
else
maintCost = (15900 * rank) + 3300;
break;
}
return maintCost;
}
}