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); private static HashMap _blueprints = new HashMap<>(); private static HashMap _doorNumbers = new HashMap<>(); public static HashMap _meshLookup = 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; } }