Rewrote pet back end to use a more authentic system

Pets can now stack
Fixes a bug where a pet could get reset to 0 hunger and growth when dropped
Player save version migration messages are no longer shown
Pets now morph into adults in-place
This commit is contained in:
Player Name
2024-10-06 10:47:36 +00:00
committed by Ryan
parent 6b0f942598
commit 9c202aa47a
19 changed files with 204 additions and 684 deletions
+1 -1
View File
@@ -18,7 +18,7 @@ class ServerConstants {
var NOAUTH_DEFAULT_ADMIN: Boolean = true
@JvmField
var CURRENT_SAVEFILE_VERSION = 1
var CURRENT_SAVEFILE_VERSION = 2
@JvmField
var DAILY_ACCOUNT_LIMIT = 3
+36
View File
@@ -326,6 +326,7 @@ fun addItem(player: Player, id: Int, amount: Int = 1, container: Container = Con
* @param player the player whose container to modify
* @param slot the slot to use
* @param item the item to replace the slot with
* @param currentItem the current item that is being replaced
* @param container the Container to modify
* @return the item that was previously in the slot, or null if none.
*/
@@ -358,6 +359,41 @@ fun replaceSlot(player: Player, slot: Int, item: Item, currentItem: Item? = null
return null
}
/**
* Replaces all items a player owns anywhere (equipment, inventory, bank, second bank)
* @param player the player whose inventory to remove the item from
* @param itemId the item ID to replace
* @param replaceId the replacement item ID
* @author Player Name
*/
fun replaceAllItems(player: Player, itemId: Int, replaceId: Int) {
val item = Item(itemId)
for (container in arrayOf(player.inventory, player.equipment, player.bankPrimary, player.bankSecondary)) {
val hasItems = container.getAll(item)
if (!item.definition.isStackable && (container == player.inventory || container == player.equipment)) {
for (target in hasItems) {
val newItem = Item(replaceId, target.amount)
container.replace(newItem, target.slot, true)
}
} else {
if (hasItems.size > 0) {
val target = hasItems[0]
var count = 0
for (x in hasItems) {
count += x.amount
}
val newItem = Item(replaceId, count)
container.replace(newItem, target.slot, true)
}
if (hasItems.size > 1) {
for (i in 1 until hasItems.size) {
container.remove(hasItems[i], hasItems[i].slot, true)
}
}
}
}
}
/**
* Add an item with a variable quantity or drop it if a player does not have enough space
* @param player the player whose inventory to add to
@@ -1,94 +0,0 @@
package core.game.node.entity.npc;
import core.cache.def.impl.NPCDefinition;
import core.game.dialogue.DialoguePlugin;
import content.global.skill.summoning.familiar.Familiar;
import content.global.skill.summoning.pet.Pets;
import core.game.interaction.OptionHandler;
import core.game.node.Node;
import core.game.node.entity.player.Player;
import core.game.node.item.Item;
import core.plugin.Plugin;
import core.plugin.ClassScanner;
import core.tools.RandomFunction;
/**
* A superclass plugin for any pets that have a metamorphosis option.
* @author Empathy
*
*/
public abstract class Metamorphosis extends OptionHandler {
/**
* The ids of the possible npcs to metamorph into.
*/
protected int[] ids;
/**
* Constructs a new {@code Metamorphosis} {@code Object}.
* @param ids the id to transform.
*/
public Metamorphosis(int...ids) {
this.ids = ids;
}
/**
* The dialogue plugin for the pet.
* @return the plugin.
*/
public abstract DialoguePlugin getDialoguePlugin();
@Override
public Plugin<Object> newInstance(Object arg) throws Throwable {
for (int id : getIds()) {
NPCDefinition.forId(id).getHandlers().put("option:metamorphosis", this);
}
if (getDialoguePlugin() != null) {
ClassScanner.definePlugin(getDialoguePlugin());
}
return this;
}
@Override
public boolean handle(Player player, Node node, String option) {
Familiar familiar = (Familiar) node;
switch (option) {
case "metamorphosis":
if (player.getFamiliarManager().isOwner(familiar)) {
int newNpc = player.getFamiliarManager().getFamiliar().getId();
while (newNpc == player.getFamiliarManager().getFamiliar().getId()) {
newNpc = getRandomNpcId();
}
for (Pets p : Pets.values()) {
if (p.getBabyNpcId() == newNpc) {
player.getFamiliarManager().morphPet(new Item(p.getBabyItemId()), false, player.getFamiliarManager().getFamiliar().getLocation());
break;
}
}
player.getPacketDispatch().sendMessage("You transform your " + player.getFamiliarManager().getFamiliar().getName() + "!");
} else {
player.getPacketDispatch().sendMessage("This is not your familiar.");
}
break;
}
return true;
}
/**
* Gets a random npc id.
* @return
*/
public int getRandomNpcId() {
int i = RandomFunction.getRandom(getIds().length - 1);
return getIds()[i];
}
/**
* Gets the npc ids.
* @return the id.
*/
public int[] getIds() {
return ids;
}
}
@@ -53,6 +53,7 @@ class PlayerSaveParser(val player: Player) {
}
fun parseData() {
parseVersion()
parseCore()
parseAttributes()
parseSkills()
@@ -77,7 +78,6 @@ class PlayerSaveParser(val player: Player) {
parseStatistics()
parseAchievements()
parsePouches()
parseVersion()
}
fun runContentHooks()
@@ -36,6 +36,7 @@ class PlayerSaver (val player: Player){
}
fun populate(): JSONObject {
val saveFile = JSONObject()
saveVersion(saveFile)
saveCoreData(saveFile)
saveSkills(saveFile)
saveSettings(saveFile)
@@ -56,7 +57,6 @@ class PlayerSaver (val player: Player){
saveStatManager(saveFile)
saveAttributes(saveFile)
savePouches(saveFile)
saveVersion(saveFile)
contentHooks.forEach { it.savePlayer(player, saveFile) }
return saveFile
}
@@ -278,17 +278,21 @@ class PlayerSaver (val player: Player){
fun saveFamiliarManager(root: JSONObject){
val familiarManager = JSONObject()
val petDetails = JSONArray()
val petDetails = JSONObject()
player.familiarManager.petDetails.map {
val detail = JSONObject()
detail.put("petId",it.key.toString())
detail.put("hunger",it.value.hunger.toString())
detail.put("growth",it.value.growth.toString())
petDetails.add(detail)
val petId = it.key
val petData = JSONArray()
for (v in it.value) {
val pet = JSONObject()
pet.put("hunger",v.hunger.toString())
pet.put("growth",v.growth.toString())
petData.add(pet)
}
petDetails.put(petId.toString(), petData)
}
familiarManager.put("petDetails",petDetails)
if(player.familiarManager.hasPet()){
familiarManager.put("currentPet",(player.familiarManager.familiar as Pet).getItemIdHash().toString())
familiarManager.put("currentPet",(player.familiarManager.familiar as Pet).getItemId().toString())
} else if (player.familiarManager.hasFamiliar()){
val familiar = JSONObject()
familiar.put("originalId",player.familiarManager.familiar.originalId.toString())
@@ -1,25 +1,22 @@
package core.game.node.entity.player.info.login
import content.global.skill.summoning.pet.Pets
import core.ServerConstants
import core.api.*
import core.game.node.entity.player.Player
import core.game.node.item.Item
import org.rs09.consts.Items
/**
* Runs one-time save-version-related hooks.
* @author Player Name
*/
class SaveVersionHooks : LoginListener {
override fun login(player: Player) {
if (player.version < ServerConstants.CURRENT_SAVEFILE_VERSION) {
sendMessage(player, "<col=CC6600>Migrating save file version ${player.version} to current save file version ${ServerConstants.CURRENT_SAVEFILE_VERSION}.</col>")
// Perform actual migrations
if (player.version < 1) { // GL #1811
if (player.version < 1) { // GL !1811
// Give out crafting hoods if the player bought any crafting capes when the hoods were not obtainable
var hasHoods = 0
var hasCapes = 0
@@ -60,10 +57,18 @@ class SaveVersionHooks : LoginListener {
}
}
// Finish up
if (player.version < 2) { //GL !1799
// Most of the migration for this MR happens in FamiliarManager.java, but we fix up any pet items here
val pets = Pets.values()
for (pet in pets) {
for (id in arrayOf(pet.babyItemId, pet.grownItemId, pet.overgrownItemId)) {
replaceAllItems(player, id, id)
// The trick here is that replaceAllItems ignores the item charge value, and will hence cause it to be lost, making all pets authentically stack again
}
}
}
player.version = ServerConstants.CURRENT_SAVEFILE_VERSION
sendMessage(player, "<col=CC6600>Save file migration complete. Happy scaping!</col>")
}
}
}
@@ -196,14 +196,6 @@ public class Item extends Node{
return idHash;
}
/**
* Sets the id hash.
* @param hash the hash to set
*/
public void setIdHash(int hash) {
this.idHash = hash;
}
/**
* Checks if the item has a wrapper plugin.
* @return {@code True} if so.