package content.global.skill.summoning.familiar; import content.global.skill.summoning.pet.Pet; import content.global.skill.summoning.pet.Pets; import core.cache.def.impl.ItemDefinition; import core.game.component.Component; import org.json.simple.JSONArray; import org.json.simple.JSONObject; import core.game.node.entity.skill.Skills; import content.global.skill.summoning.SummoningPouch; import content.global.skill.summoning.pet.PetDetails; import core.game.node.entity.combat.BattleState; import core.game.node.entity.player.Player; import core.game.node.item.Item; import core.game.world.map.Location; import core.game.world.map.zone.ZoneRestriction; import core.game.world.update.flag.context.Animation; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; import static core.api.ContentAPIKt.*; /** * Handles a player's familiar. * @author Emperor * @author Player Name */ public final class FamiliarManager { /** * The familiars mapping. */ private static final Map FAMILIARS = new HashMap<>(); /** * The pet details mapping. */ private final Map> petDetails = new HashMap<>(); /** * The player. */ private final Player player; /** * The familiar. */ private Familiar familiar; /** * The combat level difference when using summoning. */ private int summoningCombatLevel; /** * If the player has a summoning pouch. */ private boolean hasPouch; /** * Constructs a new {@code FamiliarManager} {@code Object}. * @param player The player. */ public FamiliarManager(Player player) { this.player = player; } public void parse(JSONObject familiarData) { int currentPet = -1; if (familiarData.containsKey("currentPet")) { currentPet = Integer.parseInt(familiarData.get("currentPet").toString()); } if (player.version < 2) { //migrate the v1 format for (Pets pet : Pets.values()) { for (int id : new int[]{pet.getBabyItemId(), pet.getGrownItemId(), pet.getOvergrownItemId()}) { if (id != -1) { petDetails.put(id, new ArrayList()); } } } JSONArray petDetails = (JSONArray) familiarData.get("petDetails"); for (Object petDetail : petDetails) { JSONObject detail = (JSONObject) petDetail; PetDetails details = new PetDetails(0); details.updateHunger(Double.parseDouble(detail.get("hunger").toString())); details.updateGrowth(Double.parseDouble(detail.get("growth").toString())); int itemId; int itemIdHash = Integer.parseInt(detail.get("petId").toString()); // The below is for migrating the v0 format, which stored baby item IDs + growth stages if (detail.containsKey("stage")) { // The itemIdHash is actually the baby item ID. The "stage" gives the actual pet stage we want. int babyItemId = itemIdHash; itemId = babyItemId; int stage = Integer.parseInt(detail.get("stage").toString()); if (stage > 0) { Pets pets = Pets.forId(babyItemId); itemId = pets.getNextStageItemId(itemId); if (stage > 1) { itemId = pets.getNextStageItemId(itemId); } } } else { itemId = itemIdHash >> 16 & 0xFFFF; //in the legacy v1 format, was hash rather than item id } this.petDetails.get(itemId).add(details); } if (currentPet > 65536) { currentPet = currentPet >> 16 & 0xFFFF; //in the legacy v1 format, was hash rather than item id } } else { JSONObject petDetails = (JSONObject) familiarData.get("petDetails"); for (Object key : petDetails.keySet()) { int itemId = Integer.parseInt(key.toString()); this.petDetails.put(itemId, new ArrayList<>()); JSONArray values = (JSONArray) petDetails.get(key.toString()); for (Object petDetail : values) { JSONObject detail = (JSONObject) petDetail; PetDetails details = new PetDetails(0); details.updateHunger(Double.parseDouble(detail.get("hunger").toString())); details.updateGrowth(Double.parseDouble(detail.get("growth").toString())); this.petDetails.get(itemId).add(details); } } } if (currentPet != -1) { int last = this.petDetails.get(currentPet).size() - 1; PetDetails details = this.petDetails.get(currentPet).get(last); Pets pets = Pets.forId(currentPet); familiar = new Pet(player, details, currentPet, pets.getNpcId(currentPet)); } else if (familiarData.containsKey("familiar")) { JSONObject currentFamiliar = (JSONObject) familiarData.get("familiar"); int familiarId = Integer.parseInt( currentFamiliar.get("originalId").toString()); familiar = FAMILIARS.get(familiarId).construct(player,familiarId); familiar.ticks = Integer.parseInt( currentFamiliar.get("ticks").toString()); familiar.specialPoints = Integer.parseInt( currentFamiliar.get("specialPoints").toString()); JSONArray famInv = (JSONArray) currentFamiliar.get("inventory"); if (famInv != null) { ((BurdenBeast) familiar).container.parse(famInv); } familiar.setAttribute("hp",Integer.parseInt( currentFamiliar.get("lifepoints").toString())); } } /** * Called when the player logs in. */ public void login() { if (hasFamiliar()) { familiar.init(); } player.getFamiliarManager().setConfig(243269632); } /** * Summons a familiar. * @param item The item. * @param pet If the familiar is a pet. * @param deleteItem we should delete the item. */ public void summon(Item item, boolean pet, boolean deleteItem) { boolean renew = false; if (hasFamiliar()) { if (familiar.getPouchId() == item.getId()) { renew = true; } else { player.getPacketDispatch().sendMessage("You already have a follower."); return; } } if (player.getZoneMonitor().isRestricted(ZoneRestriction.FOLLOWERS) && !player.getLocks().isLocked("enable_summoning")) { player.getPacketDispatch().sendMessage("This is a Summoning-free area."); return; } if (pet) { summonPet(item, deleteItem); return; } final SummoningPouch pouch = SummoningPouch.get(item.getId()); if (pouch == null) { return; } if (player.getSkills().getStaticLevel(Skills.SUMMONING) < pouch.getLevelRequired()) { player.getPacketDispatch().sendMessage("You need a Summoning level of " + pouch.getLevelRequired() + " to summon this familiar."); return; } if (player.getSkills().getLevel(Skills.SUMMONING) < pouch.getSummonCost()) { player.getPacketDispatch().sendMessage("You need at least " + pouch.getSummonCost() + " Summoning points to summon this familiar."); return; } final int npcId = pouch.getNpcId(); Familiar fam = !renew ? FAMILIARS.get(npcId) : familiar; if (fam == null) { player.getPacketDispatch().sendMessage("Invalid familiar " + npcId + " - report on 2009Scape GitLab"); return; } if (!renew) { fam = fam.construct(player, npcId); if (fam.getSpawnLocation() == null) { player.getPacketDispatch().sendMessage("The spirit in this pouch is too big to summon here. You will need to move to a larger"); player.getPacketDispatch().sendMessage("area."); return; } } if (!player.getInventory().remove(item)) { return; } player.getSkills().updateLevel(Skills.SUMMONING, -pouch.getSummonCost(), 0); player.getSkills().addExperience(Skills.SUMMONING, pouch.getSummonExperience()); if (!renew) { familiar = fam; spawnFamiliar(); } else { familiar.refreshTimer(); } player.getAppearance().sync(); } /** * Summons a familiar. * @param item the item. * @param pet the pet. */ public void summon(final Item item, boolean pet) { summon(item, pet, true); } /** * Morphs a pet. * @param item the item. * @param deleteItem the item. * @param location the location. */ public void morphPet(final Item item, boolean deleteItem, Location location, double hunger, double growth) { int hasWarned = ((Pet) familiar).getHasWarned(); familiar.dismiss(); summonPet(item, deleteItem, true, location, hasWarned, hunger, growth); } /** * Summons a pet. * @param item the item. * @param deleteItem the item. */ private boolean summonPet(final Item item, boolean deleteItem) { return summonPet(item, deleteItem, false, null, 0, -1, -1); } /** * Summons a pet. * @param item the item. * @param morph the pet. */ private boolean summonPet(final Item item, boolean deleteItem, boolean morph, Location location, int hasWarned, double hunger, double growth) { final int itemId = item.getId(); if (itemId > 8850 && itemId < 8900) { return false; } Pets pets = Pets.forId(itemId); if (pets == null) { return false; } if (player.getSkills().getStaticLevel(Skills.SUMMONING) < pets.getSummoningLevel()) { player.getDialogueInterpreter().sendDialogue("You need a summoning level of " + pets.getSummoningLevel() + " to summon this."); return false; } if (!this.petDetails.containsKey(itemId)) { petDetails.put(itemId, new ArrayList()); } int last = this.petDetails.get(itemId).size() - 1; if (last < 0) { //new pet last = 0; PetDetails details = new PetDetails(pets.getGrowthRate() == 0.0 ? 100.0 : 0.0); this.petDetails.get(itemId).add(details); } PetDetails details = this.petDetails.get(itemId).get(last); int npcId = pets.getNpcId(itemId); if (npcId > 0) { familiar = new Pet(player, details, itemId, npcId); ((Pet) familiar).setHasWarned(hasWarned); if (hunger != -1) ((Pet) familiar).getDetails().setHunger(hunger); if (growth != -1) ((Pet) familiar).getDetails().setGrowth(growth); if (deleteItem) { player.animate(new Animation(827)); if (!player.getInventory().remove(item, true)) { return false; } } if (morph) { morphFamiliar(location); } else { spawnFamiliar(); } return true; } return true; } /** * Morphs the current familiar. * @param location the location. */ public void morphFamiliar(Location location) { familiar.init(location, false); player.getInterfaceManager().openTab(new Component(662)); player.getInterfaceManager().setViewedTab(7); } /** * Spawns the current familiar. */ public void spawnFamiliar() { familiar.init(); player.getInterfaceManager().openTab(new Component(662)); player.getInterfaceManager().setViewedTab(7); } /** * Makes the pet eat. * @param foodId The food item id. * @param npc The pet NPC. */ public void eat(int foodId, Pet npc) { if (npc != familiar) { player.getPacketDispatch().sendMessage("This isn't your pet!"); return; } Pet pet = (Pet) familiar; Pets pets = Pets.forId(pet.getItemId()); if (pets == null) { return; } for (int food : pets.getFood()) { if (food == foodId) { player.getInventory().remove(new Item(foodId)); player.getPacketDispatch().sendMessage("Your pet happily eats the " + ItemDefinition.forId(food).getName() + "."); player.animate(new Animation(827)); npc.getDetails().updateHunger(-15.0); return; } } player.getPacketDispatch().sendMessage("Nothing interesting happens."); } /** * Picks up a pet. */ public void pickup() { if (player.getInventory().freeSlots() == 0) { player.getPacketDispatch().sendMessage("You don't have enough room in your inventory."); return; } Pet pet = ((Pet) familiar); if (player.getInventory().add(new Item(pet.getItemId()))) { player.animate(Animation.create(827)); player.getFamiliarManager().dismiss(); } } /** * Adjusts the battle state. * @param state the state. */ public void adjustBattleState(final BattleState state) { if (!hasFamiliar()) { return; } familiar.adjustPlayerBattle(state); } /** * Gets a boost from a familiar. * @param skill the skill. * @return the boosted level. */ public int getBoost(int skill) { if (!hasFamiliar()) { return 0; } return familiar.getBoost(skill); } /** * Checks if the player has an active familiar. * @return {@code True} if so. */ public boolean hasFamiliar() { return familiar != null; } /** * Checks if the player has an active familiar and is a pet. * @return {@code True} if so. */ public boolean hasPet() { return hasFamiliar() && familiar instanceof Pet; } /** * Dismisses the familiar. */ public void dismiss() { if (hasFamiliar()) { familiar.dismiss(); } } /** * Adds pet details for a new pet to that pet's stack. * @param itemId The item id of the pet. * @param details The new pet details. */ public void addDetails(int itemId, PetDetails details) { petDetails.get(itemId).add(details); } /** * Removes the details for this pet. * @param itemId The item id of the pet. */ public void removeDetails(int itemId) { int last = petDetails.get(itemId).size() - 1; if (last >= 0) { petDetails.get(itemId).remove(last); } } /** * Checks if it's the owner of a familiar. * @param familiar the familiar * @return {@code True} if so. */ public boolean isOwner(Familiar familiar) { if (!hasFamiliar()) { return false; } if (this.familiar != familiar) { player.getPacketDispatch().sendMessage("This is not your familiar."); return false; } return true; } /** * Sets a config value. * @param value the value. */ public void setConfig(int value) { int current = getVarp(player, 1160); int newVal = current + value; setVarp(player, 1160, newVal); } /** * Gets the familiar. * @return The familiar. */ public Familiar getFamiliar() { return familiar; } /** * Sets the familiar. * @param familiar The familiar to set. */ public void setFamiliar(Familiar familiar) { this.familiar = familiar; } /** * Gets the familiars. * @return The familiars. */ public static Map getFamiliars() { return FAMILIARS; } /** * Gets the usingSummoning. * @return The usingSummoning. */ public boolean isUsingSummoning() { return hasPouch || (hasFamiliar() && !hasPet()); } /** * Gets the hasPouch. * @return The hasPouch. */ public boolean isHasPouch() { return hasPouch; } /** * Sets the hasPouch. * @param hasPouch The hasPouch to set. */ public void setHasPouch(boolean hasPouch) { this.hasPouch = hasPouch; } /** * Gets the summoningCombatLevel. * @return The summoningCombatLevel. */ public int getSummoningCombatLevel() { return summoningCombatLevel; } /** * Sets the summoningCombatLevel. * @param summoningCombatLevel The summoningCombatLevel to set. */ public void setSummoningCombatLevel(int summoningCombatLevel) { this.summoningCombatLevel = summoningCombatLevel; } public Map> getPetDetails() { return petDetails; } }