From 7c38339e164249cea8d1b86e41ca4fbf202fef17 Mon Sep 17 00:00:00 2001 From: Player Name Date: Wed, 4 Oct 2023 23:19:57 +0000 Subject: [PATCH] Rewrote pet management Fixed being unable to remove pets Removed redundant pet messaging --- .../summoning/familiar/FamiliarManager.java | 129 +++++++++++------- .../global/skill/summoning/pet/Pet.java | 56 +++++--- .../skill/summoning/pet/PetDetails.java | 34 ++--- .../global/skill/summoning/pet/Pets.java | 52 +++---- .../main/core/game/container/Container.java | 15 ++ .../entity/player/info/login/PlayerSaver.kt | 3 +- 6 files changed, 175 insertions(+), 114 deletions(-) diff --git a/Server/src/main/content/global/skill/summoning/familiar/FamiliarManager.java b/Server/src/main/content/global/skill/summoning/familiar/FamiliarManager.java index ce8b55e2f..38b0f6d47 100644 --- a/Server/src/main/content/global/skill/summoning/familiar/FamiliarManager.java +++ b/Server/src/main/content/global/skill/summoning/familiar/FamiliarManager.java @@ -4,6 +4,7 @@ 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 core.game.container.Container; import org.json.simple.JSONArray; import org.json.simple.JSONObject; import core.game.node.entity.skill.Skills; @@ -27,6 +28,7 @@ import static core.api.ContentAPIKt.*; /** * Handles a player's familiar. * @author Emperor + * @author Player Name */ public final class FamiliarManager { @@ -73,34 +75,58 @@ public final class FamiliarManager { this.player = player; } - public final void parse(JSONObject familiarData){ + public final void parse(JSONObject familiarData) { + int currentPet = -1; + if (familiarData.containsKey("currentPet")) { + currentPet = Integer.parseInt(familiarData.get("currentPet").toString()); + } JSONArray petDetails = (JSONArray) familiarData.get("petDetails"); - for(int i = 0 ; i < petDetails.size(); i++){ + for (int i = 0 ; i < petDetails.size(); i++) { JSONObject detail = (JSONObject) petDetails.get(i); PetDetails details = new PetDetails(0); details.updateHunger(Double.parseDouble(detail.get("hunger").toString())); details.updateGrowth(Double.parseDouble(detail.get("growth").toString())); - details.setStage(Integer.parseInt(detail.get("stage").toString())); - this.petDetails.put(Integer.parseInt(detail.get("petId").toString()),details); + int itemIdHash = Integer.parseInt(detail.get("petId").toString()); + // The below is for migrating legacy saves, 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; + int 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); + } + } + Item item = new Item(itemId); + item.setCharge(1000); //this is the default value that will correspond to the player's item + itemIdHash = item.getIdHash(); + if (currentPet != -1 && currentPet == babyItemId) { + currentPet = itemIdHash; + } + } + this.petDetails.put(itemIdHash, details); } - if(familiarData.containsKey("currentPet")){ - int currentPet = Integer.parseInt( familiarData.get("currentPet").toString()); + if (currentPet != -1) { PetDetails details = this.petDetails.get(currentPet); - Pets pets = Pets.forId(currentPet); + int itemId = currentPet >> 16 & 0xFFFF; + Pets pets = Pets.forId(itemId); if (details == null) { details = new PetDetails(pets.getGrowthRate() == 0.0 ? 100.0 : 0.0); this.petDetails.put(currentPet, details); } - familiar = new Pet(player, details, currentPet, pets.getNpcId(details.getStage())); - } else if(familiarData.containsKey("familiar")){ + familiar = new Pet(player, details, itemId, pets.getNpcId(itemId)); + } 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){ + if (famInv != null) { ((BurdenBeast) familiar).container.parse(famInv); } familiar.setAttribute("hp",Integer.parseInt( currentFamiliar.get("lifepoints").toString())); @@ -124,14 +150,14 @@ public final class FamiliarManager { * @param deleteItem we should delete the item. */ public void summon(Item item, boolean pet, boolean deleteItem) { - boolean renew = false; + boolean renew = false; if (hasFamiliar()) { - if(familiar.getPouchId() == item.getId()) { - renew = true; - } else { - player.getPacketDispatch().sendMessage("You already have a follower."); - return; - } + 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."); @@ -156,28 +182,28 @@ public final class FamiliarManager { final int npcId = pouch.getNpcId(); Familiar fam = !renew ? FAMILIARS.get(npcId) : familiar; if (fam == null) { - player.getPacketDispatch().sendMessage("Invalid familiar " + npcId + " - report on 2009scape github"); + 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(!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(); - } + if(!renew) { + familiar = fam; + spawnFamiliar(); + } else { + familiar.refreshTimer(); + } player.getAppearance().sync(); } @@ -219,6 +245,7 @@ public final class FamiliarManager { */ private boolean summonPet(final Item item, boolean deleteItem, boolean morph, Location location) { final int itemId = item.getId(); + int itemIdHash = item.getIdHash(); if (itemId > 8850 && itemId < 8900) { return false; } @@ -230,18 +257,26 @@ public final class FamiliarManager { player.getDialogueInterpreter().sendDialogue("You need a summoning level of " + pets.getSummoningLevel() + " to summon this."); return false; } - int baseItemId = pets.getBabyItemId(); - PetDetails details = petDetails.get(baseItemId); - if (details == null) { + + PetDetails details = petDetails.get(itemIdHash); + if (details == null) { //init new pet details = new PetDetails(pets.getGrowthRate() == 0.0 ? 100.0 : 0.0); - petDetails.put(baseItemId, details); + // Find an available individual slot + ArrayList taken = new ArrayList(); + Container[] searchSpace = {player.getInventory(), player.getBankPrimary(), player.getBankSecondary()}; + for (Container container : searchSpace) { + for (Item i : container.getAll(item)) { + taken.add(i.getCharge()); + } + } + int individual; + for (individual = 0; taken.contains(individual) && individual < 0xFFFF; individual++) {} + details.setIndividual(individual); + item.setCharge(individual); + itemIdHash = item.getIdHash(); //updates the hashed item to include the new "charge" value + petDetails.put(itemIdHash, details); } - int id = pets.getItemId(details.getStage()); - if (itemId != id) { - player.getPacketDispatch().sendMessage("This is not the right pet, grow the pet correctly."); - return true; - } - int npcId = pets.getNpcId(details.getStage()); + int npcId = pets.getNpcId(itemId); if (npcId > 0) { familiar = new Pet(player, details, itemId, npcId); if (deleteItem) { @@ -314,7 +349,9 @@ public final class FamiliarManager { } Pet pet = ((Pet) familiar); PetDetails details = pet.getDetails(); - if (player.getInventory().add(new Item(pet.getPet().getItemId(details.getStage())))) { + Item petItem = new Item(pet.getItemId()); + petItem.setCharge(details.getIndividual()); + if (player.getInventory().add(petItem)) { player.animate(Animation.create(827)); player.getFamiliarManager().dismiss(); } @@ -365,7 +402,7 @@ public final class FamiliarManager { */ public void dismiss(boolean saveDetails) { if (hasPet() && !saveDetails) { - removeDetails(((Pet) familiar).getItemId()); + removeDetails(((Pet) familiar).getItemIdHash()); } if (hasFamiliar()) { familiar.dismiss(); @@ -412,9 +449,9 @@ public final class FamiliarManager { * @param value the value. */ public void setConfig(int value) { - int current = getVarp(player, 1160); + int current = getVarp(player, 1160); int newVal = current + value; - setVarp(player, 1160, newVal); + setVarp(player, 1160, newVal); } /** diff --git a/Server/src/main/content/global/skill/summoning/pet/Pet.java b/Server/src/main/content/global/skill/summoning/pet/Pet.java index aa2ceca22..3222e4ec7 100644 --- a/Server/src/main/content/global/skill/summoning/pet/Pet.java +++ b/Server/src/main/content/global/skill/summoning/pet/Pet.java @@ -4,6 +4,8 @@ import content.global.skill.skillcapeperks.SkillcapePerks; import content.global.skill.summoning.familiar.Familiar; import content.global.skill.summoning.familiar.FamiliarSpecial; import core.game.node.entity.player.Player; +import core.game.node.item.Item; +import core.game.world.GameWorld; import static core.api.ContentAPIKt.*; @@ -66,10 +68,12 @@ public final class Pet extends Familiar { public void handleTickActions() { final PetDetails petDetails = details; if (getPet().getFood().length > 0) { - if(SkillcapePerks.isActive(SkillcapePerks.PET_MASTERY, owner)) { - petDetails.updateHunger(0); - } else { - petDetails.updateHunger(petDetails.getStage() == 0 ? 0.025 : 0.018); + if(!SkillcapePerks.isActive(SkillcapePerks.PET_MASTERY, owner)) { + double amount = itemId == pet.getBabyItemId() ? 0.025 : 0.018; + if (GameWorld.getSettings().isDevMode()) { + amount *= 100; + } + petDetails.updateHunger(amount); } } double hunger = petDetails.getHunger(); @@ -84,13 +88,17 @@ public final class Pet extends Familiar { if (hunger >= 100.0 && growthRate != 0 && pet.getFood().length != 0) { owner.getFamiliarManager().dismiss(false); owner.getFamiliarManager().setFamiliar(null); - setVarp(owner, 1175, 0); + setVarp(owner, 1175, 0); owner.sendMessage("Your pet has run away."); return; } double growth = petDetails.getGrowth(); - if (pet.getGrowthRate() > 0.000) { - petDetails.updateGrowth(pet.getGrowthRate()); + double growthrate = pet.getGrowthRate(); + if (growthrate > 0.000) { + if (GameWorld.getSettings().isDevMode()) { + growthrate *= 100; + } + petDetails.updateGrowth(growthrate); if (growth == 100.0) { growNextStage(); } @@ -102,31 +110,27 @@ public final class Pet extends Familiar { } else if (!getPulseManager().hasPulseRunning()) { startFollowing(); } - setVarp(owner, 1175, ((int) details.getGrowth() << 1) | ((int) details.getHunger() << 9)); + setVarp(owner, 1175, ((int) details.getGrowth() << 1) | ((int) details.getHunger() << 9)); } /** * Method used to grow the npc's next stage. */ - public final void growNextStage() { - if (details.getStage() == 3) { - return; - } + public void growNextStage() { if (pet == null) { return; } - int npcId = pet.getNpcId(details.getStage() + 1); - if (npcId < 1) { + int newItemId = pet.getNextStageItemId(itemId); + if (newItemId == -1) { + // then this pet is already overgrown return; } - details.setStage(details.getStage() + 1); - int itemId = pet.getItemId(details.getStage()); - if (pet.getNpcId(details.getStage() + 1) > 0) { - details.updateGrowth(-100.0); - } - clear(); - Pet newPet = new Pet(owner, details, itemId, npcId); + int npcId = pet.getNpcId(newItemId); + details.updateGrowth(-100.0); + Pet newPet = new Pet(owner, details, newItemId, npcId); newPet.growthRate = growthRate; + newPet.hasWarned = hasWarned; + owner.getFamiliarManager().dismiss(false); owner.getFamiliarManager().setFamiliar(newPet); owner.getPacketDispatch().sendMessage("Your pet has grown larger."); owner.getFamiliarManager().spawnFamiliar(); @@ -171,6 +175,16 @@ public final class Pet extends Familiar { return itemId; } + /** + * Gets the itemId with the individual hashed in. + * @return The itemIdHash. + */ + public int getItemIdHash() { + Item item = new Item(itemId); + item.setCharge(details.getIndividual()); + return item.getIdHash(); + } + /** * Gets the details. * @return The details. diff --git a/Server/src/main/content/global/skill/summoning/pet/PetDetails.java b/Server/src/main/content/global/skill/summoning/pet/PetDetails.java index 6bb079f44..b3e01c869 100644 --- a/Server/src/main/content/global/skill/summoning/pet/PetDetails.java +++ b/Server/src/main/content/global/skill/summoning/pet/PetDetails.java @@ -2,11 +2,10 @@ package content.global.skill.summoning.pet; -import core.game.world.GameWorld; - /** * A class containing pet details for a certain pet. * @author Emperor + * @author Player Name */ public final class PetDetails { @@ -21,9 +20,9 @@ public final class PetDetails { private double growth = 0.0; /** - * The current stage of the pet (0 - baby, 1 - grown, 2 - overgrown). + * The individual, an in principle arbitrary integer read off of the item's charge slot. */ - private int stage; + private int individual; /** * Constructs a new {@code PetDetails} {@code Object}. @@ -38,15 +37,9 @@ public final class PetDetails { * @param amount The amount. */ public void updateHunger(double amount) { - if(GameWorld.getSettings().isDevMode()){ - hunger += amount * 100; - } else { - hunger += amount; - } + hunger += amount; if (hunger < 0.0) { hunger = 0.0; - } else if (hunger > 100.0) { - hunger = 100.0; } } @@ -80,19 +73,18 @@ public final class PetDetails { } /** - * Gets the stage. - * @return The stage. + * Sets the individual. + * @param individual The individual to set. */ - public int getStage() { - return stage; + public void setIndividual(int individual) { + this.individual = individual; } /** - * Sets the stage. - * @param stage The stage to set. + * Gets the individual. + * @return The individual. */ - public void setStage(int stage) { - this.stage = stage; + public int getIndividual() { + return individual; } - -} \ No newline at end of file +} diff --git a/Server/src/main/content/global/skill/summoning/pet/Pets.java b/Server/src/main/content/global/skill/summoning/pet/Pets.java index ad7676ce2..843075d28 100644 --- a/Server/src/main/content/global/skill/summoning/pet/Pets.java +++ b/Server/src/main/content/global/skill/summoning/pet/Pets.java @@ -1,13 +1,17 @@ package content.global.skill.summoning.pet; import core.game.node.entity.player.Player; +import core.tools.Log; import java.util.HashMap; import java.util.Map; +import static core.api.ContentAPIKt.log; + /** * An enum containing all the pets and their info. * @author Emperor + * @author Player Name */ public enum Pets { @@ -446,35 +450,35 @@ public enum Pets { /** * Gets the NPC id for this pet. - * @param stage The stage of the pet. - * @return The NPc id. + * @param itemId An int giving the ID of the item for which we want to know the corresponding NPC id. + * @return The NPC id. */ - public int getNpcId(int stage) { - switch (stage) { - case 0: - return babyNpcId; - case 1: - return grownNpcId; - case 2: - return overgrownNpcId; + public int getNpcId(int itemId) { + if (itemId == babyItemId) { + return babyNpcId; } - return 0; + if (itemId == grownItemId) { + return grownNpcId; + } + if (itemId == overgrownItemId) { + return overgrownNpcId; + } + log(this.getClass(), Log.ERR, "Could not locate NPC ID for pet item " + itemId); + return -1; } /** - * Gets the item id for this pet. - * @param stage The stage of the pet. - * @return The item id. + * Gets the next growth stage's item ID for this pet. + * @param itemId An int giving the current pet's item ID. + * @return The item ID for the next growth stage, or -1 if there isn't any (i.e. pet is already overgrown). */ - public int getItemId(int stage) { - switch (stage) { - case 0: - return babyItemId; - case 1: - return grownItemId; - case 2: - return overgrownItemId; + public int getNextStageItemId(int itemId) { + if (itemId == babyItemId) { + return grownItemId; } - return 0; + if (itemId == grownItemId) { + return overgrownItemId; + } + return -1; } -} +} \ No newline at end of file diff --git a/Server/src/main/core/game/container/Container.java b/Server/src/main/core/game/container/Container.java index 58c242dba..b45f4d8f2 100644 --- a/Server/src/main/core/game/container/Container.java +++ b/Server/src/main/core/game/container/Container.java @@ -866,6 +866,21 @@ public class Container { return null; } + /** + * Gets all instances of an item, and returns them. + * @author Player Name + * @param item the item. + * @return a list of all items that were found (can be length 0). + */ + public ArrayList getAll(Item item){ + ArrayList ret = new ArrayList(); + for(Item i : items){ + if(i == null) continue; + if(item.getId() == i.getId()) ret.add(i); + } + return ret; + } + /** * Gets the next free slot. * diff --git a/Server/src/main/core/game/node/entity/player/info/login/PlayerSaver.kt b/Server/src/main/core/game/node/entity/player/info/login/PlayerSaver.kt index 49cf921a3..d9d8a0c4f 100644 --- a/Server/src/main/core/game/node/entity/player/info/login/PlayerSaver.kt +++ b/Server/src/main/core/game/node/entity/player/info/login/PlayerSaver.kt @@ -279,12 +279,11 @@ class PlayerSaver (val player: Player){ detail.put("petId",it.key.toString()) detail.put("hunger",it.value.hunger.toString()) detail.put("growth",it.value.growth.toString()) - detail.put("stage",it.value.stage.toString()) petDetails.add(detail) } familiarManager.put("petDetails",petDetails) if(player.familiarManager.hasPet()){ - familiarManager.put("currentPet",(player.familiarManager.familiar as Pet).pet.babyItemId.toString()) + familiarManager.put("currentPet",(player.familiarManager.familiar as Pet).getItemIdHash().toString()) } else if (player.familiarManager.hasFamiliar()){ val familiar = JSONObject() familiar.put("originalId",player.familiarManager.familiar.originalId.toString())