You've already forked Arcturus-Morningstar-Extended
mirror of
https://github.com/duckietm/Arcturus-Morningstar-Extended.git
synced 2026-06-20 07:26:18 +00:00
🆙 Update to 4.0.2
This commit is contained in:
@@ -3,7 +3,10 @@ package com.eu.habbo.habbohotel.items;
|
||||
import com.eu.habbo.Emulator;
|
||||
import com.eu.habbo.habbohotel.items.interactions.*;
|
||||
import com.eu.habbo.habbohotel.items.interactions.games.InteractionGameTimer;
|
||||
import com.eu.habbo.habbohotel.items.interactions.games.battlebanzai.*;
|
||||
import com.eu.habbo.habbohotel.items.interactions.games.battlebanzai.InteractionBattleBanzaiPuck;
|
||||
import com.eu.habbo.habbohotel.items.interactions.games.battlebanzai.InteractionBattleBanzaiSphere;
|
||||
import com.eu.habbo.habbohotel.items.interactions.games.battlebanzai.InteractionBattleBanzaiTeleporter;
|
||||
import com.eu.habbo.habbohotel.items.interactions.games.battlebanzai.InteractionBattleBanzaiTile;
|
||||
import com.eu.habbo.habbohotel.items.interactions.games.battlebanzai.gates.InteractionBattleBanzaiGateBlue;
|
||||
import com.eu.habbo.habbohotel.items.interactions.games.battlebanzai.gates.InteractionBattleBanzaiGateGreen;
|
||||
import com.eu.habbo.habbohotel.items.interactions.games.battlebanzai.gates.InteractionBattleBanzaiGateRed;
|
||||
@@ -48,10 +51,10 @@ import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredBlob;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraOrEval;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraRandom;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraUnseen;
|
||||
import com.eu.habbo.habbohotel.wired.highscores.WiredHighscoreManager;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.triggers.*;
|
||||
import com.eu.habbo.habbohotel.users.Habbo;
|
||||
import com.eu.habbo.habbohotel.users.HabboItem;
|
||||
import com.eu.habbo.habbohotel.wired.highscores.WiredHighscoreManager;
|
||||
import com.eu.habbo.messages.outgoing.inventory.AddHabboItemComposer;
|
||||
import com.eu.habbo.plugin.events.emulator.EmulatorLoadItemsManagerEvent;
|
||||
import com.eu.habbo.threading.runnables.QueryDeleteHabboItem;
|
||||
@@ -134,6 +137,8 @@ public class ItemManager {
|
||||
this.interactionsList.add(new ItemInteraction("pet_drink", InteractionPetDrink.class));
|
||||
this.interactionsList.add(new ItemInteraction("pet_food", InteractionPetFood.class));
|
||||
this.interactionsList.add(new ItemInteraction("pet_toy", InteractionPetToy.class));
|
||||
this.interactionsList.add(new ItemInteraction("pet_tree", InteractionPetTree.class));
|
||||
this.interactionsList.add(new ItemInteraction("pet_trampoline", InteractionPetTrampoline.class));
|
||||
this.interactionsList.add(new ItemInteraction("breeding_nest", InteractionPetBreedingNest.class));
|
||||
this.interactionsList.add(new ItemInteraction("obstacle", InteractionObstacle.class));
|
||||
this.interactionsList.add(new ItemInteraction("monsterplant_seed", InteractionMonsterPlantSeed.class));
|
||||
|
||||
+3
-3
@@ -121,9 +121,9 @@ public class InteractionPetBreedingNest extends HabboItem {
|
||||
if (this.petOne != null) {
|
||||
habbo.getClient().sendResponse(new PetPackageNameValidationComposer(this.getId(), PetPackageNameValidationComposer.CLOSE_WIDGET, ""));
|
||||
}
|
||||
if (this.petTwo.getUserId() != habbo.getHabboInfo().getId()) {
|
||||
Habbo owner = this.petTwo.getRoom().getHabbo(this.petTwo.getUserId());
|
||||
if (owner != null) {
|
||||
if (this.petTwo != null && this.petTwo.getUserId() != habbo.getHabboInfo().getId()) {
|
||||
Habbo owner = this.petTwo.getRoom() != null ? this.petTwo.getRoom().getHabbo(this.petTwo.getUserId()) : null;
|
||||
if (owner != null && owner.getClient() != null) {
|
||||
owner.getClient().sendResponse(new PetPackageNameValidationComposer(this.getId(), PetPackageNameValidationComposer.CLOSE_WIDGET, ""));
|
||||
}
|
||||
}
|
||||
|
||||
+105
-24
@@ -2,22 +2,27 @@ package com.eu.habbo.habbohotel.items.interactions.pets;
|
||||
|
||||
import com.eu.habbo.Emulator;
|
||||
import com.eu.habbo.habbohotel.achievements.AchievementManager;
|
||||
import com.eu.habbo.habbohotel.gameclients.GameClient;
|
||||
import com.eu.habbo.habbohotel.items.Item;
|
||||
import com.eu.habbo.habbohotel.items.interactions.InteractionDefault;
|
||||
import com.eu.habbo.habbohotel.pets.Pet;
|
||||
import com.eu.habbo.habbohotel.pets.PetTasks;
|
||||
import com.eu.habbo.habbohotel.pets.PetVocalsType;
|
||||
import com.eu.habbo.habbohotel.pets.RideablePet;
|
||||
import com.eu.habbo.habbohotel.rooms.Room;
|
||||
import com.eu.habbo.habbohotel.rooms.RoomUnit;
|
||||
import com.eu.habbo.habbohotel.rooms.RoomUnitStatus;
|
||||
import com.eu.habbo.habbohotel.rooms.RoomUserRotation;
|
||||
import com.eu.habbo.messages.outgoing.rooms.users.RoomUserStatusComposer;
|
||||
import com.eu.habbo.habbohotel.rooms.*;
|
||||
import com.eu.habbo.habbohotel.users.Habbo;
|
||||
import com.eu.habbo.threading.runnables.PetClearPosture;
|
||||
import com.eu.habbo.threading.runnables.RoomUnitWalkToLocation;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class InteractionPetDrink extends InteractionDefault {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(InteractionPetDrink.class);
|
||||
|
||||
public InteractionPetDrink(ResultSet set, Item baseItem) throws SQLException {
|
||||
super(set, baseItem);
|
||||
}
|
||||
@@ -26,32 +31,81 @@ public class InteractionPetDrink extends InteractionDefault {
|
||||
super(id, userId, item, extradata, limitedStack, limitedSells);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canToggle(Habbo habbo, Room room) {
|
||||
return RoomLayout.tilesAdjecent(room.getLayout().getTile(this.getX(), this.getY()), habbo.getRoomUnit().getCurrentLocation());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(GameClient client, Room room, Object[] objects) throws Exception {
|
||||
if (client == null)
|
||||
return;
|
||||
|
||||
if (!this.canToggle(client.getHabbo(), room)) {
|
||||
RoomTile closestTile = null;
|
||||
for (RoomTile tile : room.getLayout().getTilesAround(room.getLayout().getTile(this.getX(), this.getY()))) {
|
||||
if (tile.isWalkable() && (closestTile == null || closestTile.distance(client.getHabbo().getRoomUnit().getCurrentLocation()) > tile.distance(client.getHabbo().getRoomUnit().getCurrentLocation()))) {
|
||||
closestTile = tile;
|
||||
}
|
||||
}
|
||||
|
||||
if (closestTile != null && !closestTile.equals(client.getHabbo().getRoomUnit().getCurrentLocation())) {
|
||||
List<Runnable> onSuccess = new ArrayList<>();
|
||||
onSuccess.add(() -> {
|
||||
this.change(room, this.getBaseItem().getStateCount() - 1);
|
||||
});
|
||||
|
||||
client.getHabbo().getRoomUnit().setGoalLocation(closestTile);
|
||||
Emulator.getThreading().run(new RoomUnitWalkToLocation(client.getHabbo().getRoomUnit(), closestTile, room, onSuccess, new ArrayList<>()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWalkOn(RoomUnit roomUnit, Room room, Object[] objects) throws Exception {
|
||||
super.onWalkOn(roomUnit, room, objects);
|
||||
|
||||
if (this.getExtradata() == null || this.getExtradata().isEmpty())
|
||||
this.setExtradata("0");
|
||||
|
||||
// Check if there's water left (state 0 = full, higher = less water)
|
||||
int currentState = 0;
|
||||
try {
|
||||
currentState = Integer.parseInt(this.getExtradata());
|
||||
} catch (NumberFormatException e) {
|
||||
currentState = 0;
|
||||
}
|
||||
|
||||
// If water bowl is empty (state >= max states), don't allow drinking
|
||||
if (currentState >= this.getBaseItem().getStateCount() - 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
Pet pet = room.getPet(roomUnit);
|
||||
|
||||
if (pet != null) {
|
||||
// Don't let ridden pets drink
|
||||
if (pet instanceof RideablePet && ((RideablePet) pet).getRider() != null)
|
||||
return;
|
||||
if (pet != null && !(pet instanceof RideablePet && ((RideablePet) pet).getRider() != null)
|
||||
&& pet.getPetData().haveDrinkItem(this) && pet.levelThirst >= 35) {
|
||||
pet.clearPosture();
|
||||
pet.getRoomUnit().setGoalLocation(room.getLayout().getTile(this.getX(), this.getY()));
|
||||
pet.getRoomUnit().setRotation(RoomUserRotation.values()[this.getRotation()]);
|
||||
pet.getRoomUnit().clearStatus();
|
||||
pet.getRoomUnit().setStatus(RoomUnitStatus.EAT, pet.getRoomUnit().getCurrentLocation().getStackHeight() + "");
|
||||
pet.packetUpdate = true;
|
||||
|
||||
// Say drinking vocal
|
||||
pet.say(pet.getPetData().randomVocal(PetVocalsType.DRINKING));
|
||||
|
||||
if (pet.getPetData().haveDrinkItem(this)) {
|
||||
if (pet.levelThirst >= 35) {
|
||||
pet.setTask(PetTasks.EAT);
|
||||
pet.getRoomUnit().setGoalLocation(room.getLayout().getTile(this.getX(), this.getY()));
|
||||
pet.getRoomUnit().setRotation(RoomUserRotation.values()[this.getRotation()]);
|
||||
pet.getRoomUnit().clearStatus();
|
||||
pet.getRoomUnit().removeStatus(RoomUnitStatus.MOVE);
|
||||
pet.getRoomUnit().setStatus(RoomUnitStatus.EAT, "0");
|
||||
pet.addThirst(-75);
|
||||
room.sendComposer(new RoomUserStatusComposer(roomUnit).compose());
|
||||
Emulator.getThreading().run(new PetClearPosture(pet, RoomUnitStatus.EAT, null, true), 500);
|
||||
// Faster drinking - 500ms instead of 1000ms
|
||||
Emulator.getThreading().run(() -> {
|
||||
pet.addThirst(-75);
|
||||
// Increase state to show less water (+1, not -1)
|
||||
this.change(room, 1);
|
||||
pet.getRoomUnit().clearStatus();
|
||||
Emulator.getThreading().run(new PetClearPosture(pet, RoomUnitStatus.EAT, null, true), 0);
|
||||
pet.packetUpdate = true;
|
||||
}, 500);
|
||||
|
||||
AchievementManager.progressAchievement(Emulator.getGameEnvironment().getHabboManager().getHabbo(pet.getUserId()), Emulator.getGameEnvironment().getAchievementManager().getAchievement("PetFeeding"), 75);
|
||||
}
|
||||
}
|
||||
AchievementManager.progressAchievement(Emulator.getGameEnvironment().getHabboManager().getHabbo(pet.getUserId()), Emulator.getGameEnvironment().getAchievementManager().getAchievement("PetFeeding"), 75);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,4 +113,31 @@ public class InteractionPetDrink extends InteractionDefault {
|
||||
public boolean allowWiredResetState() {
|
||||
return false;
|
||||
}
|
||||
|
||||
private void change(Room room, int amount) {
|
||||
int state = 0;
|
||||
|
||||
if (this.getExtradata() == null || this.getExtradata().isEmpty()) {
|
||||
this.setExtradata("0");
|
||||
}
|
||||
|
||||
try {
|
||||
state = Integer.parseInt(this.getExtradata());
|
||||
} catch (Exception e) {
|
||||
LOGGER.error("Caught exception", e);
|
||||
}
|
||||
|
||||
state += amount;
|
||||
if (state > this.getBaseItem().getStateCount() - 1) {
|
||||
state = this.getBaseItem().getStateCount() - 1;
|
||||
}
|
||||
|
||||
if (state < 0) {
|
||||
state = 0;
|
||||
}
|
||||
|
||||
this.setExtradata(state + "");
|
||||
this.needsUpdate(true);
|
||||
room.updateItemState(this);
|
||||
}
|
||||
}
|
||||
|
||||
+13
@@ -32,6 +32,19 @@ public class InteractionPetFood extends InteractionDefault {
|
||||
if (this.getExtradata().length() == 0)
|
||||
this.setExtradata("0");
|
||||
|
||||
// Check if there's food left (state < stateCount means food remaining)
|
||||
int currentState = 0;
|
||||
try {
|
||||
currentState = Integer.parseInt(this.getExtradata());
|
||||
} catch (NumberFormatException e) {
|
||||
currentState = 0;
|
||||
}
|
||||
|
||||
// If food is empty (state >= max states), don't allow eating
|
||||
if (currentState >= this.getBaseItem().getStateCount()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Pet pet = room.getPet(roomUnit);
|
||||
|
||||
if (pet != null) {
|
||||
|
||||
+48
-13
@@ -1,15 +1,13 @@
|
||||
package com.eu.habbo.habbohotel.items.interactions.pets;
|
||||
|
||||
import com.eu.habbo.Emulator;
|
||||
import com.eu.habbo.habbohotel.gameclients.GameClient;
|
||||
import com.eu.habbo.habbohotel.items.Item;
|
||||
import com.eu.habbo.habbohotel.items.interactions.InteractionDefault;
|
||||
import com.eu.habbo.habbohotel.pets.Pet;
|
||||
import com.eu.habbo.habbohotel.pets.PetTasks;
|
||||
import com.eu.habbo.habbohotel.pets.RideablePet;
|
||||
import com.eu.habbo.habbohotel.rooms.Room;
|
||||
import com.eu.habbo.habbohotel.rooms.RoomUnit;
|
||||
import com.eu.habbo.habbohotel.rooms.RoomUnitStatus;
|
||||
import com.eu.habbo.habbohotel.rooms.RoomUserRotation;
|
||||
import com.eu.habbo.habbohotel.pets.PetVocalsType;
|
||||
import com.eu.habbo.habbohotel.rooms.*;
|
||||
import com.eu.habbo.habbohotel.users.HabboItem;
|
||||
import com.eu.habbo.threading.runnables.PetClearPosture;
|
||||
|
||||
@@ -27,17 +25,41 @@ public class InteractionPetToy extends InteractionDefault {
|
||||
this.setExtradata("0");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(GameClient client, Room room, Object[] objects) {
|
||||
// Toys are not clickable by users
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMove(Room room, RoomTile oldLocation, RoomTile newLocation) {
|
||||
this.setExtradata("0");
|
||||
room.updateItem(this);
|
||||
|
||||
for (Pet pet : room.getPetsAt(oldLocation)) {
|
||||
pet.getRoomUnit().clearStatus();
|
||||
pet.packetUpdate = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPickUp(Room room) {
|
||||
this.setExtradata("0");
|
||||
|
||||
for (RoomTile tile : this.getOccupyingTiles(room.getLayout())) {
|
||||
for (Pet pet : room.getPetsAt(tile)) {
|
||||
pet.getRoomUnit().clearStatus();
|
||||
pet.packetUpdate = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWalkOn(RoomUnit roomUnit, Room room, Object[] objects) throws Exception {
|
||||
super.onWalkOn(roomUnit, room, objects);
|
||||
|
||||
Pet pet = room.getPet(roomUnit);
|
||||
|
||||
if (pet != null) {
|
||||
// Don't let ridden pets play with toys
|
||||
if (pet instanceof RideablePet && ((RideablePet) pet).getRider() != null)
|
||||
return;
|
||||
|
||||
if (pet != null && pet.getPetData().haveToyItem(this.getBaseItem())) {
|
||||
if (pet.getEnergy() <= 35) {
|
||||
return;
|
||||
}
|
||||
@@ -46,15 +68,20 @@ public class InteractionPetToy extends InteractionDefault {
|
||||
pet.getRoomUnit().setGoalLocation(room.getLayout().getTile(this.getX(), this.getY()));
|
||||
pet.getRoomUnit().setRotation(RoomUserRotation.values()[this.getRotation()]);
|
||||
pet.getRoomUnit().clearStatus();
|
||||
pet.getRoomUnit().removeStatus(RoomUnitStatus.MOVE);
|
||||
pet.getRoomUnit().setStatus(RoomUnitStatus.PLAY, "0");
|
||||
pet.getRoomUnit().setStatus(RoomUnitStatus.PLAY, pet.getRoomUnit().getCurrentLocation().getStackHeight() + "");
|
||||
pet.packetUpdate = true;
|
||||
|
||||
// Say playful vocal
|
||||
pet.say(pet.getPetData().randomVocal(PetVocalsType.PLAYFUL));
|
||||
|
||||
HabboItem item = this;
|
||||
Emulator.getThreading().run(() -> {
|
||||
pet.addHappiness(25);
|
||||
item.setExtradata("0");
|
||||
room.updateItem(item);
|
||||
pet.getRoomUnit().clearStatus();
|
||||
new PetClearPosture(pet, RoomUnitStatus.PLAY, null, true).run();
|
||||
pet.packetUpdate = true;
|
||||
}, 2500 + (Emulator.getRandom().nextInt(20) * 500));
|
||||
this.setExtradata("1");
|
||||
room.updateItemState(this);
|
||||
@@ -69,10 +96,18 @@ public class InteractionPetToy extends InteractionDefault {
|
||||
|
||||
if (pet != null) {
|
||||
this.setExtradata("0");
|
||||
room.updateItemState(this);
|
||||
room.updateItem(this);
|
||||
pet.getRoomUnit().clearStatus();
|
||||
pet.packetUpdate = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canWalkOn(RoomUnit roomUnit, Room room, Object[] objects) {
|
||||
Pet pet = room.getPet(roomUnit);
|
||||
return roomUnit.getRoomUnitType() == RoomUnitType.PET && pet != null && pet.getPetData().haveToyItem(this.getBaseItem());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean allowWiredResetState() {
|
||||
return false;
|
||||
|
||||
+106
@@ -0,0 +1,106 @@
|
||||
package com.eu.habbo.habbohotel.items.interactions.pets;
|
||||
|
||||
import com.eu.habbo.Emulator;
|
||||
import com.eu.habbo.habbohotel.gameclients.GameClient;
|
||||
import com.eu.habbo.habbohotel.items.Item;
|
||||
import com.eu.habbo.habbohotel.items.interactions.InteractionDefault;
|
||||
import com.eu.habbo.habbohotel.pets.Pet;
|
||||
import com.eu.habbo.habbohotel.pets.PetTasks;
|
||||
import com.eu.habbo.habbohotel.rooms.*;
|
||||
import com.eu.habbo.threading.runnables.PetClearPosture;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
|
||||
public class InteractionPetTrampoline extends InteractionDefault {
|
||||
public InteractionPetTrampoline(ResultSet set, Item baseItem) throws SQLException {
|
||||
super(set, baseItem);
|
||||
this.setExtradata("0");
|
||||
}
|
||||
|
||||
public InteractionPetTrampoline(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) {
|
||||
super(id, userId, item, extradata, limitedStack, limitedSells);
|
||||
this.setExtradata("0");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(GameClient client, Room room, Object[] objects) {
|
||||
// Trampolines are not clickable by users
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMove(Room room, RoomTile oldLocation, RoomTile newLocation) {
|
||||
this.setExtradata("0");
|
||||
room.updateItem(this);
|
||||
|
||||
for (Pet pet : room.getPetsAt(oldLocation)) {
|
||||
pet.getRoomUnit().removeStatus(RoomUnitStatus.JUMP);
|
||||
pet.packetUpdate = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPickUp(Room room) {
|
||||
this.setExtradata("0");
|
||||
|
||||
for (Pet pet : room.getPetsAt(room.getLayout().getTile(this.getX(), this.getY()))) {
|
||||
pet.getRoomUnit().removeStatus(RoomUnitStatus.JUMP);
|
||||
pet.packetUpdate = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWalkOn(RoomUnit roomUnit, Room room, Object[] objects) throws Exception {
|
||||
super.onWalkOn(roomUnit, room, objects);
|
||||
|
||||
Pet pet = room.getPet(roomUnit);
|
||||
|
||||
if (pet != null && pet.getPetData().haveToyItem(this.getBaseItem()) && this.getOccupyingTiles(room.getLayout()).contains(pet.getRoomUnit().getGoal())) {
|
||||
if (pet.getEnergy() <= 35) {
|
||||
return;
|
||||
}
|
||||
|
||||
pet.clearPosture();
|
||||
pet.setTask(PetTasks.JUMP);
|
||||
pet.getRoomUnit().setStatus(RoomUnitStatus.JUMP, "");
|
||||
pet.packetUpdate = true;
|
||||
|
||||
Emulator.getThreading().run(() -> {
|
||||
new PetClearPosture(pet, RoomUnitStatus.JUMP, null, false);
|
||||
pet.getRoomUnit().setGoalLocation(room.getRandomWalkableTile());
|
||||
this.setExtradata("0");
|
||||
room.updateItemState(this);
|
||||
}, 4000);
|
||||
|
||||
pet.addHappiness(25);
|
||||
|
||||
this.setExtradata("1");
|
||||
room.updateItemState(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWalkOff(RoomUnit roomUnit, Room room, Object[] objects) throws Exception {
|
||||
super.onWalkOff(roomUnit, room, objects);
|
||||
|
||||
Pet pet = room.getPet(roomUnit);
|
||||
|
||||
if (pet != null) {
|
||||
this.setExtradata("0");
|
||||
room.updateItem(this);
|
||||
pet.getRoomUnit().removeStatus(RoomUnitStatus.JUMP);
|
||||
pet.packetUpdate = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canWalkOn(RoomUnit roomUnit, Room room, Object[] objects) {
|
||||
Pet pet = room.getPet(roomUnit);
|
||||
return roomUnit.getRoomUnitType() == RoomUnitType.PET && pet != null && pet.getPetData().haveToyItem(this.getBaseItem());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean allowWiredResetState() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
+126
@@ -0,0 +1,126 @@
|
||||
package com.eu.habbo.habbohotel.items.interactions.pets;
|
||||
|
||||
import com.eu.habbo.habbohotel.gameclients.GameClient;
|
||||
import com.eu.habbo.habbohotel.items.Item;
|
||||
import com.eu.habbo.habbohotel.items.interactions.InteractionDefault;
|
||||
import com.eu.habbo.habbohotel.pets.Pet;
|
||||
import com.eu.habbo.habbohotel.pets.PetTasks;
|
||||
import com.eu.habbo.habbohotel.pets.PetVocalsType;
|
||||
import com.eu.habbo.habbohotel.rooms.*;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
|
||||
/**
|
||||
* Interaction for pet trees (dragon tree, monkey tree, etc.)
|
||||
* Pets can hang from these and perform special actions like Ring of Fire.
|
||||
*/
|
||||
public class InteractionPetTree extends InteractionDefault {
|
||||
public InteractionPetTree(ResultSet set, Item baseItem) throws SQLException {
|
||||
super(set, baseItem);
|
||||
this.setExtradata("0");
|
||||
}
|
||||
|
||||
public InteractionPetTree(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) {
|
||||
super(id, userId, item, extradata, limitedStack, limitedSells);
|
||||
this.setExtradata("0");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(GameClient client, Room room, Object[] objects) {
|
||||
// Trees are not clickable by users
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMove(Room room, RoomTile oldLocation, RoomTile newLocation) {
|
||||
this.setExtradata("0");
|
||||
room.updateItem(this);
|
||||
|
||||
for (Pet pet : room.getPetsAt(oldLocation)) {
|
||||
pet.getRoomUnit().removeStatus(RoomUnitStatus.HANG);
|
||||
pet.getRoomUnit().removeStatus(RoomUnitStatus.SWING);
|
||||
pet.getRoomUnit().removeStatus(RoomUnitStatus.FLAME);
|
||||
pet.packetUpdate = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPickUp(Room room) {
|
||||
this.setExtradata("0");
|
||||
|
||||
for (Pet pet : room.getPetsAt(room.getLayout().getTile(this.getX(), this.getY()))) {
|
||||
pet.getRoomUnit().removeStatus(RoomUnitStatus.HANG);
|
||||
pet.getRoomUnit().removeStatus(RoomUnitStatus.SWING);
|
||||
pet.getRoomUnit().removeStatus(RoomUnitStatus.FLAME);
|
||||
pet.packetUpdate = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWalkOn(RoomUnit roomUnit, Room room, Object[] objects) throws Exception {
|
||||
super.onWalkOn(roomUnit, room, objects);
|
||||
|
||||
Pet pet = room.getPet(roomUnit);
|
||||
|
||||
// Only dragons (type 12) can use the tree
|
||||
if (pet != null && pet.getPetData().getType() == 12 && this.getOccupyingTiles(room.getLayout()).contains(pet.getRoomUnit().getGoal())) {
|
||||
if (pet.getEnergy() <= 35) {
|
||||
return;
|
||||
}
|
||||
|
||||
RoomUnitStatus task = RoomUnitStatus.HANG;
|
||||
switch (pet.getTask()) {
|
||||
case RING_OF_FIRE:
|
||||
task = RoomUnitStatus.RINGOFFIRE;
|
||||
break;
|
||||
case SWING:
|
||||
task = RoomUnitStatus.SWING;
|
||||
break;
|
||||
default:
|
||||
// Default to HANG for all other tasks
|
||||
break;
|
||||
}
|
||||
|
||||
// Pet arrived at tree - set hang status
|
||||
pet.setTask(PetTasks.FREE);
|
||||
pet.getRoomUnit().setGoalLocation(room.getLayout().getTile(this.getX(), this.getY()));
|
||||
pet.getRoomUnit().setRotation(RoomUserRotation.values()[this.getRotation()]);
|
||||
pet.getRoomUnit().clearStatus();
|
||||
pet.getRoomUnit().setStatus(task, "");
|
||||
pet.packetUpdate = true;
|
||||
|
||||
// Say playful vocal
|
||||
pet.say(pet.getPetData().randomVocal(PetVocalsType.PLAYFUL));
|
||||
|
||||
this.setExtradata("1");
|
||||
room.updateItemState(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWalkOff(RoomUnit roomUnit, Room room, Object[] objects) throws Exception {
|
||||
super.onWalkOff(roomUnit, room, objects);
|
||||
|
||||
Pet pet = room.getPet(roomUnit);
|
||||
|
||||
if (pet != null) {
|
||||
this.setExtradata("0");
|
||||
room.updateItem(this);
|
||||
pet.getRoomUnit().removeStatus(RoomUnitStatus.HANG);
|
||||
pet.getRoomUnit().removeStatus(RoomUnitStatus.SWING);
|
||||
pet.getRoomUnit().removeStatus(RoomUnitStatus.FLAME);
|
||||
pet.packetUpdate = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canWalkOn(RoomUnit roomUnit, Room room, Object[] objects) {
|
||||
Pet pet = room.getPet(roomUnit);
|
||||
return roomUnit.getRoomUnitType() == RoomUnitType.PET && pet != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean allowWiredResetState() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,9 @@ package com.eu.habbo.habbohotel.pets;
|
||||
|
||||
import com.eu.habbo.Emulator;
|
||||
import com.eu.habbo.habbohotel.achievements.AchievementManager;
|
||||
import com.eu.habbo.habbohotel.items.Item;
|
||||
import com.eu.habbo.habbohotel.items.interactions.pets.InteractionPetToy;
|
||||
import com.eu.habbo.habbohotel.items.interactions.pets.InteractionPetTree;
|
||||
import com.eu.habbo.habbohotel.rooms.*;
|
||||
import com.eu.habbo.habbohotel.users.Habbo;
|
||||
import com.eu.habbo.habbohotel.users.HabboItem;
|
||||
@@ -14,6 +17,7 @@ import com.eu.habbo.messages.outgoing.rooms.users.RoomUserRemoveComposer;
|
||||
import com.eu.habbo.messages.outgoing.rooms.users.RoomUserTalkComposer;
|
||||
import com.eu.habbo.plugin.events.pets.PetTalkEvent;
|
||||
import gnu.trove.map.hash.THashMap;
|
||||
import gnu.trove.set.hash.THashSet;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -52,6 +56,16 @@ public class Pet implements ISerialize, Runnable {
|
||||
private int stayStartedAt = 0;
|
||||
private int idleCommandTicks = 0;
|
||||
private int freeCommandTicks = -1;
|
||||
|
||||
// Command cooldown tracking to prevent spam
|
||||
private int lastCommandId = -1;
|
||||
private long lastCommandTime = 0;
|
||||
private int sameCommandCount = 0;
|
||||
|
||||
// New managers for improved pet behavior
|
||||
private PetStatsManager statsManager;
|
||||
private PetBehaviorManager behaviorManager;
|
||||
|
||||
|
||||
private PetTasks task = PetTasks.FREE;
|
||||
|
||||
@@ -78,6 +92,10 @@ public class Pet implements ISerialize, Runnable {
|
||||
this.levelThirst = set.getInt("thirst");
|
||||
this.levelHunger = set.getInt("hunger");
|
||||
this.level = PetManager.getLevel(this.experience);
|
||||
|
||||
// Initialize managers
|
||||
this.statsManager = new PetStatsManager(this);
|
||||
this.behaviorManager = new PetBehaviorManager(this);
|
||||
}
|
||||
|
||||
public Pet(int type, int race, String color, String name, int userId) {
|
||||
@@ -101,6 +119,10 @@ public class Pet implements ISerialize, Runnable {
|
||||
this.levelHunger = 0;
|
||||
this.created = Emulator.getIntUnixTimestamp();
|
||||
this.level = 1;
|
||||
|
||||
// Initialize managers
|
||||
this.statsManager = new PetStatsManager(this);
|
||||
this.behaviorManager = new PetBehaviorManager(this);
|
||||
}
|
||||
|
||||
|
||||
@@ -216,10 +238,15 @@ public class Pet implements ISerialize, Runnable {
|
||||
}
|
||||
|
||||
public void cycle() {
|
||||
// Guard clause for null room or roomUnit
|
||||
if (this.room == null || this.roomUnit == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.idleCommandTicks++;
|
||||
|
||||
int time = Emulator.getIntUnixTimestamp();
|
||||
if (this.roomUnit != null && this.task != PetTasks.RIDE) {
|
||||
if (this.task != PetTasks.RIDE) {
|
||||
if (time - this.gestureTickTimeout > 5 && this.roomUnit.hasStatus(RoomUnitStatus.GESTURE)) {
|
||||
this.roomUnit.removeStatus(RoomUnitStatus.GESTURE);
|
||||
this.packetUpdate = true;
|
||||
@@ -254,17 +281,28 @@ public class Pet implements ISerialize, Runnable {
|
||||
if (this.levelThirst > 0)
|
||||
this.levelThirst--;
|
||||
|
||||
this.addEnergy(5);
|
||||
// Check if we're about to reach max energy before adding
|
||||
int maxEnergy = PetManager.maxEnergy(this.level);
|
||||
boolean wasResting = this.energy < maxEnergy;
|
||||
|
||||
// Nest gives faster regeneration than resting on floor
|
||||
int energyGain = (this.task == PetTasks.NEST) ? 5 : 2;
|
||||
this.addEnergy(energyGain);
|
||||
|
||||
this.addHappiness(1);
|
||||
|
||||
if (this.energy == PetManager.maxEnergy(this.level)) {
|
||||
// Wake up when fully rested
|
||||
if (wasResting && this.energy >= maxEnergy) {
|
||||
this.roomUnit.removeStatus(RoomUnitStatus.LAY);
|
||||
this.roomUnit.setCanWalk(true);
|
||||
this.roomUnit.setGoalLocation(this.room.getRandomWalkableTile());
|
||||
RoomTile tile = this.room.getRandomWalkableTile();
|
||||
if (tile != null) {
|
||||
this.roomUnit.setGoalLocation(tile);
|
||||
}
|
||||
this.task = null;
|
||||
this.roomUnit.setStatus(RoomUnitStatus.GESTURE, PetGestures.ENERGY.getKey());
|
||||
this.gestureTickTimeout = time;
|
||||
this.say(this.petData.randomVocal(PetVocalsType.GENERIC_HAPPY));
|
||||
}
|
||||
} else if (this.tickTimeout >= 5) {
|
||||
if (this.levelHunger < 100)
|
||||
@@ -319,6 +357,13 @@ public class Pet implements ISerialize, Runnable {
|
||||
this.say(this.petData.randomVocal(PetVocalsType.GENERIC_HAPPY));
|
||||
} else if (this.happiness < 15) {
|
||||
this.say(this.petData.randomVocal(PetVocalsType.GENERIC_SAD));
|
||||
// When bored and has energy, try to find a toy to play with
|
||||
if (this.energy > 40 && this.task == null) {
|
||||
this.findToy();
|
||||
}
|
||||
} else if (this.happiness < 40 && this.energy > 50 && this.task == null && Emulator.getRandom().nextInt(100) < 30) {
|
||||
// 30% chance to seek toy when moderately bored
|
||||
this.findToy();
|
||||
} else if (this.levelHunger > 50) {
|
||||
this.say(this.petData.randomVocal(PetVocalsType.HUNGRY));
|
||||
this.eat();
|
||||
@@ -363,10 +408,13 @@ public class Pet implements ISerialize, Runnable {
|
||||
case PLAY_FOOTBALL:
|
||||
case PLAY_DEAD:
|
||||
case FOLLOW:
|
||||
case FOLLOW_LEFT:
|
||||
case FOLLOW_RIGHT:
|
||||
case JUMP:
|
||||
case STAND:
|
||||
case NEST:
|
||||
case RIDE:
|
||||
case STAY:
|
||||
return false;
|
||||
default:
|
||||
break;
|
||||
@@ -448,6 +496,9 @@ public class Pet implements ISerialize, Runnable {
|
||||
|
||||
|
||||
public void findNest() {
|
||||
if (this.room == null || this.room.getRoomSpecialTypes() == null || this.petData == null) {
|
||||
return;
|
||||
}
|
||||
HabboItem item = this.petData.randomNest(this.room.getRoomSpecialTypes().getNests());
|
||||
this.roomUnit.setCanWalk(true);
|
||||
if (item != null) {
|
||||
@@ -460,7 +511,37 @@ public class Pet implements ISerialize, Runnable {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Finds a suitable drink item for this pet in the current room.
|
||||
* @return The drink Item if found, null otherwise
|
||||
*/
|
||||
public Item findDrink() {
|
||||
if (this.room == null || this.room.getRoomSpecialTypes() == null || this.petData == null) {
|
||||
return null;
|
||||
}
|
||||
HabboItem drinkItem = this.petData.randomDrinkItem(this.room.getRoomSpecialTypes().getPetDrinks());
|
||||
return drinkItem != null ? drinkItem.getBaseItem() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a suitable food item for this pet in the current room.
|
||||
* @return The food Item if found, null otherwise
|
||||
*/
|
||||
public Item findFood() {
|
||||
if (this.room == null || this.room.getRoomSpecialTypes() == null || this.petData == null) {
|
||||
return null;
|
||||
}
|
||||
HabboItem foodItem = this.petData.randomFoodItem(this.room.getRoomSpecialTypes().getPetFoods());
|
||||
return foodItem != null ? foodItem.getBaseItem() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the pet walk to a drink item and drink from it.
|
||||
*/
|
||||
public void drink() {
|
||||
if (this.room == null || this.room.getRoomSpecialTypes() == null || this.petData == null) {
|
||||
return;
|
||||
}
|
||||
HabboItem item = this.petData.randomDrinkItem(this.room.getRoomSpecialTypes().getPetDrinks());
|
||||
if (item != null) {
|
||||
this.roomUnit.setCanWalk(true);
|
||||
@@ -468,26 +549,86 @@ public class Pet implements ISerialize, Runnable {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Makes the pet walk to a food item and eat from it.
|
||||
*/
|
||||
public void eat() {
|
||||
if (this.room == null || this.room.getRoomSpecialTypes() == null || this.petData == null) {
|
||||
return;
|
||||
}
|
||||
HabboItem item = this.petData.randomFoodItem(this.room.getRoomSpecialTypes().getPetFoods());
|
||||
{
|
||||
if (item != null) {
|
||||
this.roomUnit.setCanWalk(true);
|
||||
this.roomUnit.setGoalLocation(this.room.getLayout().getTile(item.getX(), item.getY()));
|
||||
}
|
||||
if (item != null) {
|
||||
this.roomUnit.setCanWalk(true);
|
||||
this.roomUnit.setGoalLocation(this.room.getLayout().getTile(item.getX(), item.getY()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void findToy() {
|
||||
HabboItem item = this.petData.randomToyItem(this.room.getRoomSpecialTypes().getPetToys());
|
||||
{
|
||||
if (item != null) {
|
||||
this.roomUnit.setCanWalk(true);
|
||||
this.roomUnit.setGoalLocation(this.room.getLayout().getTile(item.getX(), item.getY()));
|
||||
if (this.room == null || this.room.getRoomSpecialTypes() == null || this.petData == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get all pet toys in the room
|
||||
THashSet<InteractionPetToy> toys = this.room.getRoomSpecialTypes().getPetToys();
|
||||
if (toys.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// First try to find a toy this pet can use
|
||||
HabboItem item = this.petData.randomToyItem(toys);
|
||||
|
||||
// If no compatible toy found, just pick any toy in the room
|
||||
if (item == null) {
|
||||
for (InteractionPetToy toy : toys) {
|
||||
item = toy;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (item != null) {
|
||||
this.roomUnit.setCanWalk(true);
|
||||
this.setTask(PetTasks.PLAY);
|
||||
this.roomUnit.setGoalLocation(this.room.getLayout().getTile(item.getX(), item.getY()));
|
||||
this.say(this.petData.randomVocal(PetVocalsType.PLAYFUL));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a pet tree (for dragons/monkeys) and walks to it.
|
||||
* Used for hang, swing, ring of fire actions.
|
||||
*/
|
||||
public void findTree() {
|
||||
this.findPetItem(PetTasks.FREE, InteractionPetTree.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a pet item of a specific type and walks to it.
|
||||
* Used for trampolines, trees, and other special pet furniture.
|
||||
* @param task The task to set on the pet
|
||||
* @param type The class type of the item to find
|
||||
* @return true if an item was found and pet is walking to it
|
||||
*/
|
||||
public boolean findPetItem(PetTasks task, Class<? extends HabboItem> type) {
|
||||
if (this.room == null || this.room.getRoomSpecialTypes() == null || this.petData == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
HabboItem item = this.petData.randomToyHabboItem(this.room.getRoomSpecialTypes().getItemsOfType(type));
|
||||
|
||||
if (item != null) {
|
||||
this.roomUnit.setCanWalk(true);
|
||||
this.setTask(task);
|
||||
if (this.getRoomUnit().getCurrentLocation().distance(this.room.getLayout().getTile(item.getX(), item.getY())) == 0) {
|
||||
try {
|
||||
item.onWalkOn(this.getRoomUnit(), this.getRoom(), null);
|
||||
} catch (Exception ignored) {}
|
||||
return true;
|
||||
}
|
||||
this.roomUnit.setGoalLocation(this.room.getLayout().getTile(item.getX(), item.getY()));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -753,4 +894,74 @@ public class Pet implements ISerialize, Runnable {
|
||||
public void setStayStartedAt(int stayStartedAt) {
|
||||
this.stayStartedAt = stayStartedAt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the stats manager for this pet.
|
||||
* @return The PetStatsManager instance
|
||||
*/
|
||||
public PetStatsManager getStatsManager() {
|
||||
return this.statsManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the behavior manager for this pet.
|
||||
* @return The PetBehaviorManager instance
|
||||
*/
|
||||
public PetBehaviorManager getBehaviorManager() {
|
||||
return this.behaviorManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a command can be executed based on cooldown and spam prevention.
|
||||
* @param commandId The command ID to check
|
||||
* @return true if the command can be executed, false if on cooldown
|
||||
*/
|
||||
public boolean canExecuteCommand(int commandId) {
|
||||
long now = System.currentTimeMillis();
|
||||
int globalCooldownMs = Emulator.getConfig().getInt("pet.command.cooldown_ms", 2000);
|
||||
int maxSameCommandSpam = Emulator.getConfig().getInt("pet.command.max_same_spam", 3);
|
||||
int spamResetMs = Emulator.getConfig().getInt("pet.command.spam_reset_ms", 10000);
|
||||
|
||||
// Global cooldown - applies to ALL commands to prevent switching between commands
|
||||
if (now - this.lastCommandTime < globalCooldownMs) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Reset spam counter if enough time has passed
|
||||
if (now - this.lastCommandTime > spamResetMs) {
|
||||
this.sameCommandCount = 0;
|
||||
}
|
||||
|
||||
// Check if same command is being spammed
|
||||
if (commandId == this.lastCommandId) {
|
||||
this.sameCommandCount++;
|
||||
|
||||
// Pet gets annoyed if same command spammed too much
|
||||
if (this.sameCommandCount > maxSameCommandSpam) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// Different command - reset counter but still subject to global cooldown
|
||||
this.sameCommandCount = 1;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Records that a command was executed.
|
||||
* @param commandId The command ID that was executed
|
||||
*/
|
||||
public void recordCommandExecution(int commandId) {
|
||||
this.lastCommandId = commandId;
|
||||
this.lastCommandTime = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of times the same command has been repeated.
|
||||
* @return The spam count
|
||||
*/
|
||||
public int getSameCommandCount() {
|
||||
return this.sameCommandCount;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,219 @@
|
||||
package com.eu.habbo.habbohotel.pets;
|
||||
|
||||
import com.eu.habbo.Emulator;
|
||||
import com.eu.habbo.habbohotel.rooms.RoomTile;
|
||||
import com.eu.habbo.habbohotel.rooms.RoomUnitStatus;
|
||||
|
||||
/**
|
||||
* Manages pet AI behavior using a state machine pattern.
|
||||
* Handles autonomous pet actions and state transitions.
|
||||
*/
|
||||
public class PetBehaviorManager {
|
||||
private final Pet pet;
|
||||
private PetBehaviorState currentState;
|
||||
private long stateEnteredAt;
|
||||
private long lastAutonomousAction;
|
||||
|
||||
// Configurable delays
|
||||
private int autonomousActionDelay;
|
||||
private int idleWanderMinMs;
|
||||
private int idleWanderMaxMs;
|
||||
|
||||
/**
|
||||
* Represents the various behavioral states a pet can be in.
|
||||
*/
|
||||
public enum PetBehaviorState {
|
||||
IDLE, // Standing around
|
||||
WANDERING, // Random walking
|
||||
FOLLOWING, // Following owner/habbo
|
||||
EXECUTING_COMMAND, // Doing a commanded action
|
||||
EATING, // At food bowl
|
||||
DRINKING, // At water bowl
|
||||
PLAYING, // With toy
|
||||
RESTING, // In nest/laying down
|
||||
BREEDING, // In breeding box
|
||||
DEAD // Monsterplant only
|
||||
}
|
||||
|
||||
public PetBehaviorManager(Pet pet) {
|
||||
this.pet = pet;
|
||||
this.currentState = PetBehaviorState.IDLE;
|
||||
this.stateEnteredAt = System.currentTimeMillis();
|
||||
this.lastAutonomousAction = 0;
|
||||
this.loadConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads configuration values from the emulator config.
|
||||
*/
|
||||
private void loadConfig() {
|
||||
this.autonomousActionDelay = Emulator.getConfig().getInt("pet.behavior.autonomous_action_delay", 5000);
|
||||
this.idleWanderMinMs = Emulator.getConfig().getInt("pet.behavior.idle_wander_min_ms", 10000);
|
||||
this.idleWanderMaxMs = Emulator.getConfig().getInt("pet.behavior.idle_wander_max_ms", 30000);
|
||||
}
|
||||
|
||||
/**
|
||||
* Transitions the pet to a new behavior state.
|
||||
* @param newState The new state to transition to
|
||||
*/
|
||||
public void transition(PetBehaviorState newState) {
|
||||
if (this.currentState == newState) return;
|
||||
|
||||
this.onExitState(this.currentState);
|
||||
this.currentState = newState;
|
||||
this.stateEnteredAt = System.currentTimeMillis();
|
||||
this.onEnterState(newState);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when entering a new state to set up the appropriate room unit status.
|
||||
*/
|
||||
private void onEnterState(PetBehaviorState state) {
|
||||
if (this.pet.getRoomUnit() == null) return;
|
||||
|
||||
switch (state) {
|
||||
case RESTING -> {
|
||||
this.pet.getRoomUnit().setCanWalk(false);
|
||||
this.pet.getRoomUnit().setStatus(RoomUnitStatus.LAY, "0");
|
||||
}
|
||||
case EATING -> {
|
||||
this.pet.getRoomUnit().setStatus(RoomUnitStatus.EAT, "0");
|
||||
}
|
||||
case PLAYING -> {
|
||||
// Play status handled by specific toy interaction
|
||||
}
|
||||
case IDLE -> {
|
||||
// Clear any lingering action statuses
|
||||
}
|
||||
case FOLLOWING -> {
|
||||
this.pet.getRoomUnit().setCanWalk(true);
|
||||
}
|
||||
default -> {
|
||||
// No special handling needed
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when exiting a state to clean up room unit status.
|
||||
*/
|
||||
private void onExitState(PetBehaviorState state) {
|
||||
if (this.pet.getRoomUnit() == null) return;
|
||||
|
||||
switch (state) {
|
||||
case RESTING -> {
|
||||
this.pet.getRoomUnit().removeStatus(RoomUnitStatus.LAY);
|
||||
this.pet.getRoomUnit().setCanWalk(true);
|
||||
}
|
||||
case EATING -> {
|
||||
this.pet.getRoomUnit().removeStatus(RoomUnitStatus.EAT);
|
||||
}
|
||||
case PLAYING -> {
|
||||
// Play status cleanup handled by toy interaction
|
||||
}
|
||||
default -> {
|
||||
// No special cleanup needed
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes autonomous pet behavior each cycle.
|
||||
* Called every cycle to handle autonomous pet actions based on needs.
|
||||
*/
|
||||
public void processAutonomousBehavior() {
|
||||
// Rate limit autonomous actions
|
||||
if (System.currentTimeMillis() - this.lastAutonomousAction < this.autonomousActionDelay) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.pet.getRoom() == null) return;
|
||||
|
||||
PetStatsManager stats = this.pet.getStatsManager();
|
||||
if (stats == null) return;
|
||||
|
||||
// Priority-based autonomous behavior
|
||||
if (stats.needsRest() && this.currentState != PetBehaviorState.RESTING) {
|
||||
this.pet.findNest();
|
||||
this.lastAutonomousAction = System.currentTimeMillis();
|
||||
return;
|
||||
}
|
||||
|
||||
if (stats.needsFood() && this.currentState != PetBehaviorState.EATING) {
|
||||
this.pet.eat();
|
||||
this.lastAutonomousAction = System.currentTimeMillis();
|
||||
return;
|
||||
}
|
||||
|
||||
if (stats.needsWater() && this.currentState != PetBehaviorState.DRINKING) {
|
||||
this.pet.drink();
|
||||
this.lastAutonomousAction = System.currentTimeMillis();
|
||||
return;
|
||||
}
|
||||
|
||||
if (stats.needsAttention() && this.currentState == PetBehaviorState.IDLE) {
|
||||
this.pet.findToy();
|
||||
this.lastAutonomousAction = System.currentTimeMillis();
|
||||
return;
|
||||
}
|
||||
|
||||
// Random wandering when idle
|
||||
if (this.currentState == PetBehaviorState.IDLE) {
|
||||
long idleTime = System.currentTimeMillis() - this.stateEnteredAt;
|
||||
int wanderDelay = this.idleWanderMinMs + Emulator.getRandom().nextInt(
|
||||
this.idleWanderMaxMs - this.idleWanderMinMs);
|
||||
|
||||
if (idleTime > wanderDelay) {
|
||||
RoomTile tile = this.pet.getRoom().getRandomWalkableTile();
|
||||
if (tile != null && this.pet.getRoomUnit() != null) {
|
||||
this.pet.getRoomUnit().setGoalLocation(tile);
|
||||
this.transition(PetBehaviorState.WANDERING);
|
||||
}
|
||||
this.lastAutonomousAction = System.currentTimeMillis();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the pet can currently accept commands.
|
||||
* @return true if the pet can accept commands
|
||||
*/
|
||||
public boolean canAcceptCommand() {
|
||||
return this.currentState != PetBehaviorState.DEAD
|
||||
&& this.currentState != PetBehaviorState.BREEDING;
|
||||
}
|
||||
|
||||
/**
|
||||
* Interrupts the current action and returns to idle state.
|
||||
*/
|
||||
public void interruptCurrentAction() {
|
||||
if (this.currentState == PetBehaviorState.EXECUTING_COMMAND
|
||||
|| this.currentState == PetBehaviorState.WANDERING) {
|
||||
this.transition(PetBehaviorState.IDLE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current behavior state.
|
||||
* @return The current PetBehaviorState
|
||||
*/
|
||||
public PetBehaviorState getCurrentState() {
|
||||
return this.currentState;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the timestamp when the current state was entered.
|
||||
* @return Timestamp in milliseconds
|
||||
*/
|
||||
public long getStateEnteredAt() {
|
||||
return this.stateEnteredAt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets how long the pet has been in the current state.
|
||||
* @return Duration in milliseconds
|
||||
*/
|
||||
public long getTimeInCurrentState() {
|
||||
return System.currentTimeMillis() - this.stateEnteredAt;
|
||||
}
|
||||
}
|
||||
@@ -45,31 +45,75 @@ public class PetCommand implements Comparable<PetCommand> {
|
||||
}
|
||||
|
||||
public void handle(Pet pet, Habbo habbo, String[] data) {
|
||||
if (Emulator.getRandom().nextInt((pet.level - this.level <= 0 ? 2 : pet.level - this.level) + 2) == 0) {
|
||||
pet.say(pet.petData.randomVocal(PetVocalsType.DISOBEY));
|
||||
// Check command cooldown to prevent spam (global cooldown for ALL commands)
|
||||
if (!pet.canExecuteCommand(this.id)) {
|
||||
// Pet ignores spammed commands - maybe give a tired/annoyed response occasionally
|
||||
if (pet.getSameCommandCount() > Emulator.getConfig().getInt("pet.command.max_same_spam", 3)) {
|
||||
if (Emulator.getRandom().nextInt(3) == 0) {
|
||||
pet.say(pet.getPetData().randomVocal(PetVocalsType.DISOBEY));
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if pet has enough energy to perform the command
|
||||
int minEnergy = Emulator.getConfig().getInt("pet.command.min_energy", 15);
|
||||
if (pet.getEnergy() < minEnergy || pet.getEnergy() < this.energyCost) {
|
||||
pet.say(pet.getPetData().randomVocal(PetVocalsType.TIRED));
|
||||
pet.recordCommandExecution(this.id);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if pet is too unhappy to obey
|
||||
int minHappiness = Emulator.getConfig().getInt("pet.command.min_happiness", 10);
|
||||
if (pet.getHappiness() < minHappiness) {
|
||||
pet.say(pet.getPetData().randomVocal(PetVocalsType.GENERIC_SAD));
|
||||
pet.recordCommandExecution(this.id);
|
||||
return;
|
||||
}
|
||||
|
||||
// Improved obedience formula - configurable base chance with level scaling
|
||||
int levelDifference = pet.getLevel() - this.level;
|
||||
int baseChance = Emulator.getConfig().getInt("pet.command.base_obey_chance", 70); // 70% base
|
||||
int levelBonus = Math.max(0, levelDifference * 5); // +5% per level above requirement
|
||||
int obeyChance = Math.min(95, baseChance + levelBonus); // Cap at 95%
|
||||
|
||||
if (Emulator.getRandom().nextInt(100) >= obeyChance) {
|
||||
pet.say(pet.getPetData().randomVocal(PetVocalsType.DISOBEY));
|
||||
pet.recordCommandExecution(this.id);
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.action != null) {
|
||||
if (this.action.petTask != pet.getTask()) {
|
||||
if (this.action.stopsPetWalking) {
|
||||
pet.getRoomUnit().setGoalLocation(pet.getRoomUnit().getCurrentLocation());
|
||||
// Allow repeating actions - removed the task comparison check
|
||||
if (this.action.stopsPetWalking) {
|
||||
pet.getRoomUnit().setGoalLocation(pet.getRoomUnit().getCurrentLocation());
|
||||
}
|
||||
if (this.action.apply(pet, habbo, data)) {
|
||||
// Set the pet's task from the action
|
||||
if (this.action.petTask != null) {
|
||||
pet.setTask(this.action.petTask);
|
||||
}
|
||||
if (this.action.apply(pet, habbo, data)) {
|
||||
for (RoomUnitStatus status : this.action.statusToRemove) {
|
||||
pet.getRoomUnit().removeStatus(status);
|
||||
}
|
||||
|
||||
for (RoomUnitStatus status : this.action.statusToSet) {
|
||||
pet.getRoomUnit().setStatus(status, "0");
|
||||
}
|
||||
|
||||
pet.getRoomUnit().setStatus(RoomUnitStatus.GESTURE, this.action.gestureToSet);
|
||||
|
||||
pet.addEnergy(-this.energyCost);
|
||||
pet.addHappiness(-this.happinessCost);
|
||||
pet.addExperience(this.xp);
|
||||
|
||||
for (RoomUnitStatus status : this.action.statusToRemove) {
|
||||
pet.getRoomUnit().removeStatus(status);
|
||||
}
|
||||
|
||||
for (RoomUnitStatus status : this.action.statusToSet) {
|
||||
pet.getRoomUnit().setStatus(status, "0");
|
||||
}
|
||||
|
||||
pet.getRoomUnit().setStatus(RoomUnitStatus.GESTURE, this.action.gestureToSet);
|
||||
|
||||
pet.addEnergy(-this.energyCost);
|
||||
pet.addHappiness(-this.happinessCost);
|
||||
pet.addExperience(this.xp);
|
||||
|
||||
// Mark pet for status update so clients see the animation
|
||||
pet.packetUpdate = true;
|
||||
|
||||
// Record successful command execution
|
||||
pet.recordCommandExecution(this.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,15 +97,22 @@ public class PetData implements Comparable<PetData> {
|
||||
|
||||
|
||||
boolean haveNest(Item nest) {
|
||||
// If no nest items are registered, allow all nest items
|
||||
if (this.nestItems.isEmpty() && PetData.generalNestItems.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
return PetData.generalNestItems.contains(nest) || this.nestItems.contains(nest);
|
||||
}
|
||||
|
||||
|
||||
public HabboItem randomNest(THashSet<InteractionNest> items) {
|
||||
List<HabboItem> nestList = new ArrayList<>();
|
||||
|
||||
// If no nest items are registered, allow all nests in the room
|
||||
boolean allowAll = this.nestItems.isEmpty() && PetData.generalNestItems.isEmpty();
|
||||
|
||||
for (InteractionNest nest : items) {
|
||||
if (this.haveNest(nest)) {
|
||||
if (allowAll || this.haveNest(nest)) {
|
||||
nestList.add(nest);
|
||||
}
|
||||
}
|
||||
@@ -136,15 +143,22 @@ public class PetData implements Comparable<PetData> {
|
||||
|
||||
|
||||
boolean haveFoodItem(Item food) {
|
||||
// If no food items are registered, allow all food items
|
||||
if (this.foodItems.isEmpty() && PetData.generalFoodItems.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
return this.foodItems.contains(food) || PetData.generalFoodItems.contains(food);
|
||||
}
|
||||
|
||||
|
||||
public HabboItem randomFoodItem(THashSet<InteractionPetFood> items) {
|
||||
List<HabboItem> foodList = new ArrayList<>();
|
||||
|
||||
// If no food items are registered, allow all food in the room
|
||||
boolean allowAll = this.foodItems.isEmpty() && PetData.generalFoodItems.isEmpty();
|
||||
|
||||
for (InteractionPetFood food : items) {
|
||||
if (this.haveFoodItem(food)) {
|
||||
if (allowAll || this.haveFoodItem(food)) {
|
||||
foodList.add(food);
|
||||
}
|
||||
}
|
||||
@@ -174,15 +188,22 @@ public class PetData implements Comparable<PetData> {
|
||||
|
||||
|
||||
boolean haveDrinkItem(Item item) {
|
||||
// If no drink items are registered, allow all drink items
|
||||
if (this.drinkItems.isEmpty() && PetData.generalDrinkItems.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
return this.drinkItems.contains(item) || PetData.generalDrinkItems.contains(item);
|
||||
}
|
||||
|
||||
|
||||
public HabboItem randomDrinkItem(THashSet<InteractionPetDrink> items) {
|
||||
List<HabboItem> drinkList = new ArrayList<>();
|
||||
|
||||
// If no drink items are registered, allow all drinks in the room
|
||||
boolean allowAll = this.drinkItems.isEmpty() && PetData.generalDrinkItems.isEmpty();
|
||||
|
||||
for (InteractionPetDrink drink : items) {
|
||||
if (this.haveDrinkItem(drink)) {
|
||||
if (allowAll || this.haveDrinkItem(drink)) {
|
||||
drinkList.add(drink);
|
||||
}
|
||||
}
|
||||
@@ -212,15 +233,22 @@ public class PetData implements Comparable<PetData> {
|
||||
|
||||
|
||||
public boolean haveToyItem(Item toy) {
|
||||
// If no toy items are registered, allow all toy items
|
||||
if (this.toyItems.isEmpty() && PetData.generalToyItems.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
return this.toyItems.contains(toy) || PetData.generalToyItems.contains(toy);
|
||||
}
|
||||
|
||||
|
||||
public HabboItem randomToyItem(THashSet<InteractionPetToy> toys) {
|
||||
List<HabboItem> toyList = new ArrayList<>();
|
||||
|
||||
// If no toy items are registered, allow all toys in the room
|
||||
boolean allowAll = this.toyItems.isEmpty() && PetData.generalToyItems.isEmpty();
|
||||
|
||||
for (InteractionPetToy toy : toys) {
|
||||
if (this.haveToyItem(toy)) {
|
||||
if (allowAll || this.haveToyItem(toy)) {
|
||||
toyList.add(toy);
|
||||
}
|
||||
}
|
||||
@@ -233,6 +261,30 @@ public class PetData implements Comparable<PetData> {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a random toy item from a generic set of HabboItems.
|
||||
* Used for finding pet items like trampolines, trees, etc.
|
||||
*/
|
||||
public HabboItem randomToyHabboItem(THashSet<HabboItem> items) {
|
||||
List<HabboItem> itemList = new ArrayList<>();
|
||||
|
||||
// If no toy items are registered, allow all toys in the room
|
||||
boolean allowAll = this.toyItems.isEmpty() && PetData.generalToyItems.isEmpty();
|
||||
|
||||
for (HabboItem item : items) {
|
||||
if (allowAll || this.haveToyItem(item)) {
|
||||
itemList.add(item);
|
||||
}
|
||||
}
|
||||
|
||||
if (!itemList.isEmpty()) {
|
||||
Collections.shuffle(itemList);
|
||||
return itemList.get(0);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public PetVocal randomVocal(PetVocalsType type) {
|
||||
THashSet<PetVocal> petTypeVocals = this.petVocals.get(type);
|
||||
@@ -242,8 +294,10 @@ public class PetData implements Comparable<PetData> {
|
||||
int generalSize = generalVocals != null ? generalVocals.size() : 0;
|
||||
int totalSize = petTypeSize + generalSize;
|
||||
|
||||
if (totalSize == 0)
|
||||
return null;
|
||||
if (totalSize == 0) {
|
||||
// Return a default vocal instead of null
|
||||
return getDefaultVocal(type);
|
||||
}
|
||||
|
||||
int randomIndex = Emulator.getRandom().nextInt(totalSize);
|
||||
|
||||
@@ -262,7 +316,31 @@ public class PetData implements Comparable<PetData> {
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
return getDefaultVocal(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a default vocal message when no configured vocals exist for the type.
|
||||
* This prevents null pointer exceptions and silent pets.
|
||||
*/
|
||||
private static PetVocal getDefaultVocal(PetVocalsType type) {
|
||||
return switch (type) {
|
||||
case GENERIC_HAPPY -> new PetVocal("*wags tail happily*");
|
||||
case GENERIC_SAD -> new PetVocal("*whimpers*");
|
||||
case GENERIC_NEUTRAL -> new PetVocal("*looks around*");
|
||||
case HUNGRY -> new PetVocal("*stomach growls*");
|
||||
case THIRSTY -> new PetVocal("*pants*");
|
||||
case TIRED -> new PetVocal("*yawns*");
|
||||
case SLEEPING -> new PetVocal("*snores softly*");
|
||||
case PLAYFUL -> new PetVocal("*bounces excitedly*");
|
||||
case DISOBEY -> new PetVocal("*ignores command*");
|
||||
case EATING -> new PetVocal("*munches happily*");
|
||||
case DRINKING -> new PetVocal("*laps up water*");
|
||||
case LEVEL_UP -> new PetVocal("*jumps with joy*");
|
||||
case GREET_OWNER -> new PetVocal("*perks up excitedly*");
|
||||
case MUTED -> new PetVocal("*stays quiet*");
|
||||
case UNKNOWN_COMMAND -> new PetVocal("*tilts head confused*");
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -7,6 +7,7 @@ import com.eu.habbo.habbohotel.items.interactions.pets.InteractionNest;
|
||||
import com.eu.habbo.habbohotel.items.interactions.pets.InteractionPetDrink;
|
||||
import com.eu.habbo.habbohotel.items.interactions.pets.InteractionPetFood;
|
||||
import com.eu.habbo.habbohotel.items.interactions.pets.InteractionPetToy;
|
||||
import com.eu.habbo.habbohotel.items.interactions.pets.InteractionPetTrampoline;
|
||||
import com.eu.habbo.habbohotel.pets.actions.*;
|
||||
import com.eu.habbo.habbohotel.rooms.Room;
|
||||
import com.eu.habbo.habbohotel.rooms.RoomTile;
|
||||
@@ -52,6 +53,12 @@ public class PetManager {
|
||||
this.put(15, new ActionFollowLeft());
|
||||
this.put(16, new ActionFollowRight());
|
||||
this.put(17, new ActionPlayFootball());
|
||||
this.put(18, new ActionTeleport());
|
||||
this.put(19, new ActionBounce());
|
||||
this.put(20, new ActionFlatten());
|
||||
this.put(21, new ActionDance());
|
||||
this.put(22, new ActionSpin());
|
||||
this.put(23, new ActionSwitch());
|
||||
this.put(24, new ActionMoveForward());
|
||||
this.put(25, new ActionTurnLeft());
|
||||
this.put(26, new ActionTurnRight());
|
||||
@@ -59,10 +66,20 @@ public class PetManager {
|
||||
this.put(28, new ActionCroak());
|
||||
this.put(29, new ActionDip());
|
||||
this.put(30, new ActionWave());
|
||||
this.put(31, new ActionMambo());
|
||||
this.put(32, new ActionHighJump());
|
||||
this.put(33, new ActionChickenDance());
|
||||
this.put(34, new ActionTripleJump());
|
||||
this.put(35, new ActionWings());
|
||||
this.put(36, new ActionBreatheFire());
|
||||
this.put(37, new ActionHang());
|
||||
this.put(38, new ActionTorch());
|
||||
this.put(40, new ActionSwing());
|
||||
this.put(41, new ActionRoll());
|
||||
this.put(42, new ActionRingOfFire());
|
||||
this.put(43, new ActionEat());
|
||||
this.put(44, new ActionWagTail());
|
||||
this.put(45, new ActionCount());
|
||||
this.put(46, new ActionBreed());
|
||||
|
||||
}
|
||||
@@ -200,7 +217,7 @@ public class PetManager {
|
||||
PetData.generalFoodItems.add(baseItem);
|
||||
else if (baseItem.getInteractionType().getType() == InteractionPetDrink.class)
|
||||
PetData.generalDrinkItems.add(baseItem);
|
||||
else if (baseItem.getInteractionType().getType() == InteractionPetToy.class)
|
||||
else if (baseItem.getInteractionType().getType() == InteractionPetToy.class || baseItem.getInteractionType().getType() == InteractionPetTrampoline.class)
|
||||
PetData.generalToyItems.add(baseItem);
|
||||
} else {
|
||||
PetData data = this.getPetData(set.getInt("pet_id"));
|
||||
@@ -212,7 +229,7 @@ public class PetManager {
|
||||
data.addFoodItem(baseItem);
|
||||
else if (baseItem.getInteractionType().getType() == InteractionPetDrink.class)
|
||||
data.addDrinkItem(baseItem);
|
||||
else if (baseItem.getInteractionType().getType() == InteractionPetToy.class)
|
||||
else if (baseItem.getInteractionType().getType() == InteractionPetToy.class || baseItem.getInteractionType().getType() == InteractionPetTrampoline.class)
|
||||
data.addToyItem(baseItem);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
package com.eu.habbo.habbohotel.pets;
|
||||
|
||||
/**
|
||||
* Represents the various mood states a pet can be in based on its stats.
|
||||
*/
|
||||
public enum PetMood {
|
||||
EXHAUSTED("exhausted", 0),
|
||||
STARVING("starving", 1),
|
||||
PARCHED("parched", 2),
|
||||
DEPRESSED("depressed", 3),
|
||||
NEUTRAL("neutral", 4),
|
||||
HAPPY("happy", 5),
|
||||
ECSTATIC("ecstatic", 6);
|
||||
|
||||
private final String key;
|
||||
private final int priority;
|
||||
|
||||
PetMood(String key, int priority) {
|
||||
this.key = key;
|
||||
this.priority = priority;
|
||||
}
|
||||
|
||||
public String getKey() {
|
||||
return this.key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the priority of this mood. Lower values indicate more urgent moods.
|
||||
* @return The priority value
|
||||
*/
|
||||
public int getPriority() {
|
||||
return this.priority;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this mood is a negative/urgent mood that needs addressing.
|
||||
* @return true if this is a negative mood
|
||||
*/
|
||||
public boolean isNegative() {
|
||||
return this.priority <= 3;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this mood is a positive mood.
|
||||
* @return true if this is a positive mood
|
||||
*/
|
||||
public boolean isPositive() {
|
||||
return this.priority >= 5;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
package com.eu.habbo.habbohotel.pets;
|
||||
|
||||
import com.eu.habbo.Emulator;
|
||||
|
||||
/**
|
||||
* Manages all stat-related logic for pets including decay rates, recovery rates,
|
||||
* and mood calculations. This centralizes stat management for better maintainability.
|
||||
*/
|
||||
public class PetStatsManager {
|
||||
private final Pet pet;
|
||||
|
||||
// Configurable decay rates
|
||||
private int hungerDecayRate;
|
||||
private int thirstDecayRate;
|
||||
private int energyDecayRate;
|
||||
private int happinessDecayRate;
|
||||
|
||||
// Configurable recovery rates
|
||||
private int energyRecoveryRate;
|
||||
private int happinessRecoveryRate;
|
||||
|
||||
// Configurable thresholds
|
||||
private int hungryThreshold;
|
||||
private int thirstyThreshold;
|
||||
private int tiredThreshold;
|
||||
private int sadThreshold;
|
||||
|
||||
public PetStatsManager(Pet pet) {
|
||||
this.pet = pet;
|
||||
this.loadConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads configuration values from the emulator config.
|
||||
*/
|
||||
private void loadConfig() {
|
||||
this.hungerDecayRate = Emulator.getConfig().getInt("pet.stats.hunger_decay", 1);
|
||||
this.thirstDecayRate = Emulator.getConfig().getInt("pet.stats.thirst_decay", 1);
|
||||
this.energyDecayRate = Emulator.getConfig().getInt("pet.stats.energy_decay", 1);
|
||||
this.happinessDecayRate = Emulator.getConfig().getInt("pet.stats.happiness_decay", 1);
|
||||
this.energyRecoveryRate = Emulator.getConfig().getInt("pet.stats.energy_recovery", 5);
|
||||
this.happinessRecoveryRate = Emulator.getConfig().getInt("pet.stats.happiness_recovery", 1);
|
||||
|
||||
this.hungryThreshold = Emulator.getConfig().getInt("pet.threshold.hungry", 50);
|
||||
this.thirstyThreshold = Emulator.getConfig().getInt("pet.threshold.thirsty", 50);
|
||||
this.tiredThreshold = Emulator.getConfig().getInt("pet.threshold.tired", 30);
|
||||
this.sadThreshold = Emulator.getConfig().getInt("pet.threshold.sad", 30);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process stat changes when pet is walking/active.
|
||||
*/
|
||||
public void processActiveTick() {
|
||||
this.pet.addHunger(this.hungerDecayRate);
|
||||
this.pet.addThirst(this.thirstDecayRate);
|
||||
this.pet.addEnergy(-this.energyDecayRate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process stat changes when pet is in nest/down (resting).
|
||||
*/
|
||||
public void processRestingTick() {
|
||||
this.pet.addHunger(-1);
|
||||
this.pet.addThirst(-1);
|
||||
this.pet.addEnergy(this.energyRecoveryRate);
|
||||
this.pet.addHappiness(this.happinessRecoveryRate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process stat changes when pet is standing still/idle.
|
||||
*/
|
||||
public void processIdleTick() {
|
||||
this.pet.addHunger(this.hungerDecayRate / 2);
|
||||
this.pet.addThirst(this.thirstDecayRate / 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current mood of the pet based on its stats.
|
||||
* @return The current PetMood
|
||||
*/
|
||||
public PetMood getCurrentMood() {
|
||||
if (this.pet.getEnergy() < 20) return PetMood.EXHAUSTED;
|
||||
if (this.pet.getLevelHunger() > 80) return PetMood.STARVING;
|
||||
if (this.pet.getLevelThirst() > 80) return PetMood.PARCHED;
|
||||
if (this.pet.getHappiness() < 20) return PetMood.DEPRESSED;
|
||||
if (this.pet.getHappiness() > 80 && this.pet.getEnergy() > 60) return PetMood.ECSTATIC;
|
||||
if (this.pet.getHappiness() > 50) return PetMood.HAPPY;
|
||||
return PetMood.NEUTRAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the pet needs food.
|
||||
* @return true if hunger level exceeds the hungry threshold
|
||||
*/
|
||||
public boolean needsFood() {
|
||||
return this.pet.getLevelHunger() > this.hungryThreshold;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the pet needs water.
|
||||
* @return true if thirst level exceeds the thirsty threshold
|
||||
*/
|
||||
public boolean needsWater() {
|
||||
return this.pet.getLevelThirst() > this.thirstyThreshold;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the pet needs rest.
|
||||
* @return true if energy level is below the tired threshold
|
||||
*/
|
||||
public boolean needsRest() {
|
||||
return this.pet.getEnergy() < this.tiredThreshold;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the pet needs attention/play.
|
||||
* @return true if happiness level is below the sad threshold
|
||||
*/
|
||||
public boolean needsAttention() {
|
||||
return this.pet.getHappiness() < this.sadThreshold;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the overall health score of the pet (0-100).
|
||||
* @return An integer representing overall pet health
|
||||
*/
|
||||
public int getOverallHealth() {
|
||||
int maxEnergy = PetManager.maxEnergy(this.pet.getLevel());
|
||||
int energyPercent = (this.pet.getEnergy() * 100) / maxEnergy;
|
||||
int hungerPercent = 100 - this.pet.getLevelHunger();
|
||||
int thirstPercent = 100 - this.pet.getLevelThirst();
|
||||
int happinessPercent = this.pet.getHappiness();
|
||||
|
||||
return (energyPercent + hungerPercent + thirstPercent + happinessPercent) / 4;
|
||||
}
|
||||
|
||||
// Getters for decay/recovery rates
|
||||
public int getHungerDecayRate() { return hungerDecayRate; }
|
||||
public int getThirstDecayRate() { return thirstDecayRate; }
|
||||
public int getEnergyDecayRate() { return energyDecayRate; }
|
||||
public int getHappinessDecayRate() { return happinessDecayRate; }
|
||||
public int getEnergyRecoveryRate() { return energyRecoveryRate; }
|
||||
public int getHappinessRecoveryRate() { return happinessRecoveryRate; }
|
||||
|
||||
// Getters for thresholds
|
||||
public int getHungryThreshold() { return hungryThreshold; }
|
||||
public int getThirstyThreshold() { return thirstyThreshold; }
|
||||
public int getTiredThreshold() { return tiredThreshold; }
|
||||
public int getSadThreshold() { return sadThreshold; }
|
||||
}
|
||||
@@ -17,6 +17,9 @@ public class ActionBeg extends PetAction {
|
||||
public boolean apply(Pet pet, Habbo habbo, String[] data) {
|
||||
pet.clearPosture();
|
||||
|
||||
// Begging is fun interaction with owner
|
||||
pet.addHappiness(5);
|
||||
|
||||
if (pet.getHappiness() > 90)
|
||||
pet.say(pet.getPetData().randomVocal(PetVocalsType.PLAYFUL));
|
||||
else
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
package com.eu.habbo.habbohotel.pets.actions;
|
||||
|
||||
import com.eu.habbo.Emulator;
|
||||
import com.eu.habbo.habbohotel.pets.Pet;
|
||||
import com.eu.habbo.habbohotel.pets.PetAction;
|
||||
import com.eu.habbo.habbohotel.pets.PetTasks;
|
||||
import com.eu.habbo.habbohotel.pets.PetVocalsType;
|
||||
import com.eu.habbo.habbohotel.rooms.RoomUnitStatus;
|
||||
import com.eu.habbo.habbohotel.users.Habbo;
|
||||
import com.eu.habbo.threading.runnables.PetClearPosture;
|
||||
|
||||
public class ActionBounce extends PetAction {
|
||||
public ActionBounce() {
|
||||
super(PetTasks.JUMP, true);
|
||||
this.minimumActionDuration = 3000;
|
||||
this.statusToSet.add(RoomUnitStatus.JUMP);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Pet pet, Habbo habbo, String[] data) {
|
||||
pet.clearPosture();
|
||||
|
||||
Emulator.getThreading().run(new PetClearPosture(pet, RoomUnitStatus.JUMP, null, false), 3000);
|
||||
|
||||
// Bouncing is fun!
|
||||
pet.addHappiness(8);
|
||||
|
||||
if (pet.getHappiness() > 60)
|
||||
pet.say(pet.getPetData().randomVocal(PetVocalsType.PLAYFUL));
|
||||
else
|
||||
pet.say(pet.getPetData().randomVocal(PetVocalsType.GENERIC_NEUTRAL));
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import com.eu.habbo.habbohotel.items.interactions.pets.InteractionPetBreedingNes
|
||||
import com.eu.habbo.habbohotel.pets.Pet;
|
||||
import com.eu.habbo.habbohotel.pets.PetAction;
|
||||
import com.eu.habbo.habbohotel.pets.PetTasks;
|
||||
import com.eu.habbo.habbohotel.pets.PetVocalsType;
|
||||
import com.eu.habbo.habbohotel.users.Habbo;
|
||||
import com.eu.habbo.habbohotel.users.HabboItem;
|
||||
import com.eu.habbo.messages.outgoing.rooms.pets.breeding.PetBreedingStartFailedComposer;
|
||||
@@ -28,6 +29,7 @@ public class ActionBreed extends PetAction {
|
||||
|
||||
if (nest != null) {
|
||||
pet.getRoomUnit().setGoalLocation(pet.getRoom().getLayout().getTile(nest.getX(), nest.getY()));
|
||||
pet.say(pet.getPetData().randomVocal(PetVocalsType.GENERIC_HAPPY));
|
||||
|
||||
return true;
|
||||
} else {
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.eu.habbo.habbohotel.pets.actions;
|
||||
|
||||
import com.eu.habbo.Emulator;
|
||||
import com.eu.habbo.habbohotel.pets.Pet;
|
||||
import com.eu.habbo.habbohotel.pets.PetAction;
|
||||
import com.eu.habbo.habbohotel.pets.PetTasks;
|
||||
import com.eu.habbo.habbohotel.pets.PetVocalsType;
|
||||
import com.eu.habbo.habbohotel.rooms.RoomUnitStatus;
|
||||
import com.eu.habbo.habbohotel.users.Habbo;
|
||||
import com.eu.habbo.threading.runnables.PetClearPosture;
|
||||
|
||||
public class ActionChickenDance extends PetAction {
|
||||
public ActionChickenDance() {
|
||||
super(PetTasks.FREE, true);
|
||||
this.minimumActionDuration = 4000;
|
||||
this.statusToSet.add(RoomUnitStatus.DANCE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Pet pet, Habbo habbo, String[] data) {
|
||||
pet.clearPosture();
|
||||
pet.getRoomUnit().setStatus(RoomUnitStatus.DANCE, "");
|
||||
|
||||
Emulator.getThreading().run(new PetClearPosture(pet, RoomUnitStatus.DANCE, null, false), 4000);
|
||||
|
||||
if (pet.getHappiness() > 50)
|
||||
pet.say(pet.getPetData().randomVocal(PetVocalsType.PLAYFUL));
|
||||
else
|
||||
pet.say(pet.getPetData().randomVocal(PetVocalsType.GENERIC_NEUTRAL));
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.eu.habbo.habbohotel.pets.actions;
|
||||
|
||||
import com.eu.habbo.habbohotel.pets.Pet;
|
||||
import com.eu.habbo.habbohotel.pets.PetAction;
|
||||
import com.eu.habbo.habbohotel.pets.PetTasks;
|
||||
import com.eu.habbo.habbohotel.pets.PetVocalsType;
|
||||
import com.eu.habbo.habbohotel.users.Habbo;
|
||||
|
||||
public class ActionCount extends PetAction {
|
||||
public ActionCount() {
|
||||
super(PetTasks.FREE, true);
|
||||
this.minimumActionDuration = 3000;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Pet pet, Habbo habbo, String[] data) {
|
||||
pet.clearPosture();
|
||||
|
||||
// Count by speaking
|
||||
if (pet.getHappiness() > 50)
|
||||
pet.say(pet.getPetData().randomVocal(PetVocalsType.PLAYFUL));
|
||||
else
|
||||
pet.say(pet.getPetData().randomVocal(PetVocalsType.GENERIC_NEUTRAL));
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,8 @@ public class ActionCroak extends PetAction {
|
||||
|
||||
if (pet.getHappiness() > 80)
|
||||
pet.say(pet.getPetData().randomVocal(PetVocalsType.PLAYFUL));
|
||||
else
|
||||
pet.say(pet.getPetData().randomVocal(PetVocalsType.GENERIC_NEUTRAL));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.eu.habbo.habbohotel.pets.actions;
|
||||
|
||||
import com.eu.habbo.Emulator;
|
||||
import com.eu.habbo.habbohotel.pets.Pet;
|
||||
import com.eu.habbo.habbohotel.pets.PetAction;
|
||||
import com.eu.habbo.habbohotel.pets.PetTasks;
|
||||
import com.eu.habbo.habbohotel.pets.PetVocalsType;
|
||||
import com.eu.habbo.habbohotel.rooms.RoomUnitStatus;
|
||||
import com.eu.habbo.habbohotel.users.Habbo;
|
||||
import com.eu.habbo.threading.runnables.PetClearPosture;
|
||||
|
||||
public class ActionDance extends PetAction {
|
||||
public ActionDance() {
|
||||
super(PetTasks.FREE, true);
|
||||
this.minimumActionDuration = 5000;
|
||||
this.statusToSet.add(RoomUnitStatus.DANCE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Pet pet, Habbo habbo, String[] data) {
|
||||
pet.clearPosture();
|
||||
pet.getRoomUnit().setStatus(RoomUnitStatus.DANCE, "");
|
||||
|
||||
Emulator.getThreading().run(new PetClearPosture(pet, RoomUnitStatus.DANCE, null, false), 5000);
|
||||
|
||||
// Dancing is fun!
|
||||
pet.addHappiness(10);
|
||||
|
||||
if (pet.getHappiness() > 60)
|
||||
pet.say(pet.getPetData().randomVocal(PetVocalsType.PLAYFUL));
|
||||
else
|
||||
pet.say(pet.getPetData().randomVocal(PetVocalsType.GENERIC_NEUTRAL));
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import com.eu.habbo.Emulator;
|
||||
import com.eu.habbo.habbohotel.items.interactions.InteractionWater;
|
||||
import com.eu.habbo.habbohotel.pets.Pet;
|
||||
import com.eu.habbo.habbohotel.pets.PetAction;
|
||||
import com.eu.habbo.habbohotel.pets.PetVocalsType;
|
||||
import com.eu.habbo.habbohotel.users.Habbo;
|
||||
import com.eu.habbo.habbohotel.users.HabboItem;
|
||||
import gnu.trove.set.hash.THashSet;
|
||||
@@ -23,6 +24,8 @@ public class ActionDip extends PetAction {
|
||||
HabboItem waterPatch = (HabboItem) waterItems.toArray()[Emulator.getRandom().nextInt(waterItems.size())];
|
||||
|
||||
pet.getRoomUnit().setGoalLocation(pet.getRoom().getLayout().getTile(waterPatch.getX(), waterPatch.getY()));
|
||||
|
||||
pet.say(pet.getPetData().randomVocal(PetVocalsType.PLAYFUL));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.eu.habbo.habbohotel.pets.actions;
|
||||
|
||||
import com.eu.habbo.Emulator;
|
||||
import com.eu.habbo.habbohotel.pets.Pet;
|
||||
import com.eu.habbo.habbohotel.pets.PetAction;
|
||||
import com.eu.habbo.habbohotel.pets.PetTasks;
|
||||
@@ -10,18 +11,33 @@ import com.eu.habbo.habbohotel.users.Habbo;
|
||||
public class ActionDown extends PetAction {
|
||||
public ActionDown() {
|
||||
super(PetTasks.DOWN, true);
|
||||
this.minimumActionDuration = 4000;
|
||||
this.statusToRemove.add(RoomUnitStatus.BEG);
|
||||
this.statusToRemove.add(RoomUnitStatus.MOVE);
|
||||
this.statusToRemove.add(RoomUnitStatus.SIT);
|
||||
this.statusToRemove.add(RoomUnitStatus.DEAD);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Pet pet, Habbo habbo, String[] data) {
|
||||
pet.getRoomUnit().setStatus(RoomUnitStatus.LAY, pet.getRoom().getStackHeight(pet.getRoomUnit().getX(), pet.getRoomUnit().getY(), false) + "");
|
||||
if (pet.getTask() != PetTasks.DOWN && !pet.getRoomUnit().hasStatus(RoomUnitStatus.LAY)) {
|
||||
pet.getRoomUnit().cmdLay = true;
|
||||
pet.getRoomUnit().setStatus(RoomUnitStatus.LAY, pet.getRoomUnit().getCurrentLocation().getStackHeight() + "");
|
||||
|
||||
if (pet.getHappiness() > 50)
|
||||
pet.say(pet.getPetData().randomVocal(PetVocalsType.PLAYFUL));
|
||||
else
|
||||
pet.say(pet.getPetData().randomVocal(PetVocalsType.GENERIC_NEUTRAL));
|
||||
// Lying down is a bit boring but restful
|
||||
pet.addHappiness(-2);
|
||||
|
||||
Emulator.getThreading().run(() -> {
|
||||
pet.getRoomUnit().cmdLay = false;
|
||||
pet.clearPosture();
|
||||
}, this.minimumActionDuration);
|
||||
|
||||
if (pet.getHappiness() > 50)
|
||||
pet.say(pet.getPetData().randomVocal(PetVocalsType.PLAYFUL));
|
||||
else
|
||||
pet.say(pet.getPetData().randomVocal(PetVocalsType.GENERIC_NEUTRAL));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.eu.habbo.habbohotel.pets.actions;
|
||||
|
||||
import com.eu.habbo.habbohotel.items.Item;
|
||||
import com.eu.habbo.habbohotel.pets.Pet;
|
||||
import com.eu.habbo.habbohotel.pets.PetAction;
|
||||
import com.eu.habbo.habbohotel.pets.PetVocalsType;
|
||||
@@ -12,17 +13,30 @@ public class ActionDrink extends PetAction {
|
||||
|
||||
@Override
|
||||
public boolean apply(Pet pet, Habbo habbo, String[] data) {
|
||||
if (pet.getLevelThirst() > 40) {
|
||||
pet.drink();
|
||||
|
||||
if (pet.getLevelThirst() > 65)
|
||||
pet.say(pet.getPetData().randomVocal(PetVocalsType.THIRSTY));
|
||||
|
||||
return true;
|
||||
// Check if pet is thirsty enough to want water (threshold: 35 to match InteractionPetDrink)
|
||||
if (pet.getLevelThirst() >= 35) {
|
||||
// Check if there's water available in the room before sending pet to drink
|
||||
if (pet.getRoom() != null && pet.getRoom().getRoomSpecialTypes() != null) {
|
||||
Item drinkItem = pet.findDrink();
|
||||
|
||||
if (drinkItem != null) {
|
||||
// Water exists - pet goes to drink
|
||||
if (pet.getLevelThirst() > 65) {
|
||||
pet.say(pet.getPetData().randomVocal(PetVocalsType.THIRSTY));
|
||||
}
|
||||
pet.drink();
|
||||
return true;
|
||||
} else {
|
||||
// No suitable water in room - pet complains
|
||||
pet.say(pet.getPetData().randomVocal(PetVocalsType.THIRSTY));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
// Pet is not thirsty - disobeys command
|
||||
pet.say(pet.getPetData().randomVocal(PetVocalsType.DISOBEY));
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,33 +1,42 @@
|
||||
package com.eu.habbo.habbohotel.pets.actions;
|
||||
|
||||
import com.eu.habbo.Emulator;
|
||||
import com.eu.habbo.habbohotel.items.Item;
|
||||
import com.eu.habbo.habbohotel.pets.Pet;
|
||||
import com.eu.habbo.habbohotel.pets.PetAction;
|
||||
import com.eu.habbo.habbohotel.pets.PetVocalsType;
|
||||
import com.eu.habbo.habbohotel.rooms.RoomUnitStatus;
|
||||
import com.eu.habbo.habbohotel.users.Habbo;
|
||||
import com.eu.habbo.threading.runnables.PetClearPosture;
|
||||
|
||||
public class ActionEat extends PetAction {
|
||||
public ActionEat() {
|
||||
super(null, true);
|
||||
|
||||
this.statusToSet.add(RoomUnitStatus.EAT);
|
||||
// stopsPetWalking=false so pet can walk to food
|
||||
// Don't set EAT status here - let InteractionPetFood.onWalkOn() handle it when pet arrives
|
||||
super(null, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Pet pet, Habbo habbo, String[] data) {
|
||||
//Eat
|
||||
if (pet.getLevelHunger() > 40) {
|
||||
pet.say(pet.getPetData().randomVocal(PetVocalsType.HUNGRY));
|
||||
Emulator.getThreading().run(new PetClearPosture(pet, RoomUnitStatus.EAT, null, false), 500);
|
||||
pet.eat();
|
||||
|
||||
return true;
|
||||
// Check if pet is hungry enough to want food (threshold: 35 to match InteractionPetFood)
|
||||
if (pet.getLevelHunger() >= 35) {
|
||||
// Check if there's food available in the room before sending pet to eat
|
||||
if (pet.getRoom() != null && pet.getRoom().getRoomSpecialTypes() != null) {
|
||||
Item foodItem = pet.findFood();
|
||||
|
||||
if (foodItem != null) {
|
||||
// Food exists - pet goes to eat
|
||||
pet.say(pet.getPetData().randomVocal(PetVocalsType.HUNGRY));
|
||||
pet.eat();
|
||||
return true;
|
||||
} else {
|
||||
// No suitable food in room - pet complains
|
||||
pet.say(pet.getPetData().randomVocal(PetVocalsType.HUNGRY));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
// Pet is not hungry - disobeys command
|
||||
pet.say(pet.getPetData().randomVocal(PetVocalsType.DISOBEY));
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.eu.habbo.habbohotel.pets.actions;
|
||||
|
||||
import com.eu.habbo.Emulator;
|
||||
import com.eu.habbo.habbohotel.pets.Pet;
|
||||
import com.eu.habbo.habbohotel.pets.PetAction;
|
||||
import com.eu.habbo.habbohotel.pets.PetTasks;
|
||||
import com.eu.habbo.habbohotel.pets.PetVocalsType;
|
||||
import com.eu.habbo.habbohotel.rooms.RoomUnitStatus;
|
||||
import com.eu.habbo.habbohotel.users.Habbo;
|
||||
import com.eu.habbo.threading.runnables.PetClearPosture;
|
||||
|
||||
public class ActionFlatten extends PetAction {
|
||||
public ActionFlatten() {
|
||||
super(PetTasks.DOWN, true);
|
||||
this.minimumActionDuration = 3000;
|
||||
this.statusToSet.add(RoomUnitStatus.FLAT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Pet pet, Habbo habbo, String[] data) {
|
||||
pet.clearPosture();
|
||||
pet.getRoomUnit().setStatus(RoomUnitStatus.FLAT, pet.getRoomUnit().getCurrentLocation().getStackHeight() + "");
|
||||
|
||||
Emulator.getThreading().run(new PetClearPosture(pet, RoomUnitStatus.FLAT, null, false), 3000);
|
||||
|
||||
if (pet.getHappiness() > 50)
|
||||
pet.say(pet.getPetData().randomVocal(PetVocalsType.PLAYFUL));
|
||||
else
|
||||
pet.say(pet.getPetData().randomVocal(PetVocalsType.GENERIC_NEUTRAL));
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,9 @@ public class ActionFollow extends PetAction {
|
||||
|
||||
Emulator.getThreading().run(new PetFollowHabbo(pet, habbo, 0));
|
||||
|
||||
// Following owner is enjoyable
|
||||
pet.addHappiness(5);
|
||||
|
||||
if (pet.getHappiness() > 75)
|
||||
pet.say(pet.getPetData().randomVocal(PetVocalsType.PLAYFUL));
|
||||
else
|
||||
|
||||
@@ -10,7 +10,7 @@ import com.eu.habbo.threading.runnables.PetFollowHabbo;
|
||||
|
||||
public class ActionFollowLeft extends PetAction {
|
||||
public ActionFollowLeft() {
|
||||
super(PetTasks.FOLLOW, true);
|
||||
super(PetTasks.FOLLOW_LEFT, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -18,7 +18,10 @@ public class ActionFollowLeft extends PetAction {
|
||||
//Follow left.
|
||||
pet.clearPosture();
|
||||
|
||||
Emulator.getThreading().run(new PetFollowHabbo(pet, habbo, -2));
|
||||
Emulator.getThreading().run(new PetFollowHabbo(pet, habbo, +2));
|
||||
|
||||
// Following owner is enjoyable
|
||||
pet.addHappiness(5);
|
||||
|
||||
if (pet.getHappiness() > 75)
|
||||
pet.say(pet.getPetData().randomVocal(PetVocalsType.PLAYFUL));
|
||||
|
||||
@@ -10,7 +10,7 @@ import com.eu.habbo.threading.runnables.PetFollowHabbo;
|
||||
|
||||
public class ActionFollowRight extends PetAction {
|
||||
public ActionFollowRight() {
|
||||
super(PetTasks.FOLLOW, true);
|
||||
super(PetTasks.FOLLOW_RIGHT, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -18,7 +18,10 @@ public class ActionFollowRight extends PetAction {
|
||||
//Follow right.
|
||||
pet.clearPosture();
|
||||
|
||||
Emulator.getThreading().run(new PetFollowHabbo(pet, habbo, +2));
|
||||
Emulator.getThreading().run(new PetFollowHabbo(pet, habbo, -2));
|
||||
|
||||
// Following owner is enjoyable
|
||||
pet.addHappiness(5);
|
||||
|
||||
if (pet.getHappiness() > 75)
|
||||
pet.say(pet.getPetData().randomVocal(PetVocalsType.PLAYFUL));
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.eu.habbo.habbohotel.pets.actions;
|
||||
import com.eu.habbo.habbohotel.pets.Pet;
|
||||
import com.eu.habbo.habbohotel.pets.PetAction;
|
||||
import com.eu.habbo.habbohotel.pets.PetTasks;
|
||||
import com.eu.habbo.habbohotel.pets.PetVocalsType;
|
||||
import com.eu.habbo.habbohotel.users.Habbo;
|
||||
|
||||
public class ActionFree extends PetAction {
|
||||
@@ -13,6 +14,9 @@ public class ActionFree extends PetAction {
|
||||
@Override
|
||||
public boolean apply(Pet pet, Habbo habbo, String[] data) {
|
||||
pet.freeCommand();
|
||||
|
||||
if (pet.getHappiness() > 50)
|
||||
pet.say(pet.getPetData().randomVocal(PetVocalsType.GENERIC_HAPPY));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
package com.eu.habbo.habbohotel.pets.actions;
|
||||
|
||||
import com.eu.habbo.Emulator;
|
||||
import com.eu.habbo.habbohotel.items.interactions.pets.InteractionPetTree;
|
||||
import com.eu.habbo.habbohotel.pets.Pet;
|
||||
import com.eu.habbo.habbohotel.pets.PetAction;
|
||||
import com.eu.habbo.habbohotel.pets.PetTasks;
|
||||
import com.eu.habbo.habbohotel.pets.PetVocalsType;
|
||||
import com.eu.habbo.habbohotel.rooms.RoomUnitStatus;
|
||||
import com.eu.habbo.habbohotel.users.Habbo;
|
||||
import com.eu.habbo.habbohotel.users.HabboItem;
|
||||
import com.eu.habbo.threading.runnables.PetClearPosture;
|
||||
|
||||
public class ActionHang extends PetAction {
|
||||
public ActionHang() {
|
||||
super(PetTasks.FREE, true);
|
||||
this.minimumActionDuration = 4000;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Pet pet, Habbo habbo, String[] data) {
|
||||
// Hang requires pet to be on a pet tree (dragon/monkey tree)
|
||||
if (pet.getRoom() == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
HabboItem itemBelow = pet.getRoom().getTopItemAt(pet.getRoomUnit().getX(), pet.getRoomUnit().getY());
|
||||
if (!(itemBelow instanceof InteractionPetTree)) {
|
||||
// Pet must go to tree first
|
||||
pet.findTree();
|
||||
pet.say(pet.getPetData().randomVocal(PetVocalsType.DISOBEY));
|
||||
return false;
|
||||
}
|
||||
|
||||
pet.clearPosture();
|
||||
pet.getRoomUnit().setStatus(RoomUnitStatus.HANG, "");
|
||||
|
||||
Emulator.getThreading().run(new PetClearPosture(pet, RoomUnitStatus.HANG, null, false), 4000);
|
||||
|
||||
if (pet.getHappiness() > 50)
|
||||
pet.say(pet.getPetData().randomVocal(PetVocalsType.PLAYFUL));
|
||||
else
|
||||
pet.say(pet.getPetData().randomVocal(PetVocalsType.GENERIC_NEUTRAL));
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import com.eu.habbo.habbohotel.pets.Pet;
|
||||
import com.eu.habbo.habbohotel.pets.PetAction;
|
||||
import com.eu.habbo.habbohotel.pets.PetTasks;
|
||||
import com.eu.habbo.habbohotel.pets.PetVocalsType;
|
||||
import com.eu.habbo.habbohotel.rooms.RoomTile;
|
||||
import com.eu.habbo.habbohotel.rooms.RoomUnitStatus;
|
||||
import com.eu.habbo.habbohotel.users.Habbo;
|
||||
|
||||
@@ -16,8 +17,27 @@ public class ActionHere extends PetAction {
|
||||
|
||||
@Override
|
||||
public boolean apply(Pet pet, Habbo habbo, String[] data) {
|
||||
pet.getRoomUnit().setGoalLocation(pet.getRoom().getLayout().getTileInFront(habbo.getRoomUnit().getCurrentLocation(), habbo.getRoomUnit().getBodyRotation().getValue()));
|
||||
pet.getRoomUnit().setCanWalk(true);
|
||||
if (pet.getRoom() == null || habbo.getRoomUnit() == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
pet.clearPosture();
|
||||
|
||||
// Try tile in front of habbo first
|
||||
RoomTile target = pet.getRoom().getLayout().getTileInFront(
|
||||
habbo.getRoomUnit().getCurrentLocation(),
|
||||
habbo.getRoomUnit().getBodyRotation().getValue()
|
||||
);
|
||||
|
||||
// If not walkable, try habbo's current tile
|
||||
if (target == null || !pet.getRoom().getLayout().tileWalkable(target.x, target.y)) {
|
||||
target = habbo.getRoomUnit().getCurrentLocation();
|
||||
}
|
||||
|
||||
if (target != null) {
|
||||
pet.getRoomUnit().setGoalLocation(target);
|
||||
pet.getRoomUnit().setCanWalk(true);
|
||||
}
|
||||
|
||||
if (pet.getHappiness() > 75)
|
||||
pet.say(pet.getPetData().randomVocal(PetVocalsType.PLAYFUL));
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.eu.habbo.habbohotel.pets.actions;
|
||||
|
||||
import com.eu.habbo.Emulator;
|
||||
import com.eu.habbo.habbohotel.pets.Pet;
|
||||
import com.eu.habbo.habbohotel.pets.PetAction;
|
||||
import com.eu.habbo.habbohotel.pets.PetTasks;
|
||||
import com.eu.habbo.habbohotel.pets.PetVocalsType;
|
||||
import com.eu.habbo.habbohotel.rooms.RoomUnitStatus;
|
||||
import com.eu.habbo.habbohotel.users.Habbo;
|
||||
import com.eu.habbo.threading.runnables.PetClearPosture;
|
||||
|
||||
public class ActionHighJump extends PetAction {
|
||||
public ActionHighJump() {
|
||||
super(PetTasks.JUMP, true);
|
||||
this.minimumActionDuration = 3000;
|
||||
this.statusToSet.add(RoomUnitStatus.JUMP);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Pet pet, Habbo habbo, String[] data) {
|
||||
pet.clearPosture();
|
||||
|
||||
Emulator.getThreading().run(new PetClearPosture(pet, RoomUnitStatus.JUMP, null, false), 3000);
|
||||
|
||||
if (pet.getHappiness() > 70)
|
||||
pet.say(pet.getPetData().randomVocal(PetVocalsType.PLAYFUL));
|
||||
else
|
||||
pet.say(pet.getPetData().randomVocal(PetVocalsType.GENERIC_NEUTRAL));
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,9 @@ public class ActionJump extends PetAction {
|
||||
|
||||
Emulator.getThreading().run(new PetClearPosture(pet, RoomUnitStatus.JUMP, null, false), 2000);
|
||||
|
||||
// Jumping is fun!
|
||||
pet.addHappiness(8);
|
||||
|
||||
if (pet.getHappiness() > 60)
|
||||
pet.say(pet.getPetData().randomVocal(PetVocalsType.PLAYFUL));
|
||||
else
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.eu.habbo.habbohotel.pets.actions;
|
||||
|
||||
import com.eu.habbo.Emulator;
|
||||
import com.eu.habbo.habbohotel.pets.Pet;
|
||||
import com.eu.habbo.habbohotel.pets.PetAction;
|
||||
import com.eu.habbo.habbohotel.pets.PetTasks;
|
||||
import com.eu.habbo.habbohotel.pets.PetVocalsType;
|
||||
import com.eu.habbo.habbohotel.rooms.RoomUnitStatus;
|
||||
import com.eu.habbo.habbohotel.users.Habbo;
|
||||
import com.eu.habbo.threading.runnables.PetClearPosture;
|
||||
|
||||
public class ActionMambo extends PetAction {
|
||||
public ActionMambo() {
|
||||
super(PetTasks.FREE, true);
|
||||
this.minimumActionDuration = 5000;
|
||||
this.statusToSet.add(RoomUnitStatus.DANCE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Pet pet, Habbo habbo, String[] data) {
|
||||
pet.clearPosture();
|
||||
pet.getRoomUnit().setStatus(RoomUnitStatus.DANCE, "");
|
||||
|
||||
Emulator.getThreading().run(new PetClearPosture(pet, RoomUnitStatus.DANCE, null, false), 5000);
|
||||
|
||||
if (pet.getHappiness() > 60)
|
||||
pet.say(pet.getPetData().randomVocal(PetVocalsType.PLAYFUL));
|
||||
else
|
||||
pet.say(pet.getPetData().randomVocal(PetVocalsType.GENERIC_NEUTRAL));
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package com.eu.habbo.habbohotel.pets.actions;
|
||||
import com.eu.habbo.habbohotel.pets.Pet;
|
||||
import com.eu.habbo.habbohotel.pets.PetAction;
|
||||
import com.eu.habbo.habbohotel.pets.PetVocalsType;
|
||||
import com.eu.habbo.habbohotel.rooms.RoomTile;
|
||||
import com.eu.habbo.habbohotel.users.Habbo;
|
||||
|
||||
public class ActionMoveForward extends PetAction {
|
||||
@@ -12,12 +13,23 @@ public class ActionMoveForward extends PetAction {
|
||||
|
||||
@Override
|
||||
public boolean apply(Pet pet, Habbo habbo, String[] data) {
|
||||
|
||||
pet.getRoomUnit().setGoalLocation(pet.getRoom().getLayout().getTileInFront(pet.getRoomUnit().getCurrentLocation(), pet.getRoomUnit().getBodyRotation().getValue()));
|
||||
pet.getRoomUnit().setCanWalk(true);
|
||||
|
||||
pet.say(pet.getPetData().randomVocal(PetVocalsType.GENERIC_NEUTRAL));
|
||||
|
||||
if (pet.getRoom() == null || pet.getRoomUnit() == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
RoomTile targetTile = pet.getRoom().getLayout().getTileInFront(
|
||||
pet.getRoomUnit().getCurrentLocation(),
|
||||
pet.getRoomUnit().getBodyRotation().getValue()
|
||||
);
|
||||
|
||||
if (targetTile != null && pet.getRoom().getLayout().tileWalkable(targetTile.x, targetTile.y)) {
|
||||
pet.getRoomUnit().setGoalLocation(targetTile);
|
||||
pet.getRoomUnit().setCanWalk(true);
|
||||
pet.say(pet.getPetData().randomVocal(PetVocalsType.GENERIC_NEUTRAL));
|
||||
return true;
|
||||
}
|
||||
|
||||
pet.say(pet.getPetData().randomVocal(PetVocalsType.DISOBEY));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,17 +12,14 @@ public class ActionNest extends PetAction {
|
||||
|
||||
@Override
|
||||
public boolean apply(Pet pet, Habbo habbo, String[] data) {
|
||||
if (pet.getEnergy() < 65) {
|
||||
pet.findNest();
|
||||
// Pet always obeys nest command - will go to nest or lay down if no nest available
|
||||
pet.findNest();
|
||||
|
||||
if (pet.getEnergy() < 30)
|
||||
pet.say(pet.getPetData().randomVocal(PetVocalsType.TIRED));
|
||||
if (pet.getEnergy() < 30)
|
||||
pet.say(pet.getPetData().randomVocal(PetVocalsType.TIRED));
|
||||
else
|
||||
pet.say(pet.getPetData().randomVocal(PetVocalsType.GENERIC_NEUTRAL));
|
||||
|
||||
return true;
|
||||
} else {
|
||||
pet.say(pet.getPetData().randomVocal(PetVocalsType.DISOBEY));
|
||||
}
|
||||
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,25 +1,133 @@
|
||||
package com.eu.habbo.habbohotel.pets.actions;
|
||||
|
||||
import com.eu.habbo.habbohotel.pets.Pet;
|
||||
import com.eu.habbo.habbohotel.pets.PetAction;
|
||||
import com.eu.habbo.habbohotel.pets.PetVocalsType;
|
||||
import com.eu.habbo.Emulator;
|
||||
import com.eu.habbo.habbohotel.items.interactions.pets.InteractionPetToy;
|
||||
import com.eu.habbo.habbohotel.pets.*;
|
||||
import com.eu.habbo.habbohotel.rooms.RoomTile;
|
||||
import com.eu.habbo.habbohotel.rooms.RoomUnitStatus;
|
||||
import com.eu.habbo.habbohotel.users.Habbo;
|
||||
import com.eu.habbo.habbohotel.users.HabboItem;
|
||||
import gnu.trove.set.hash.THashSet;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class ActionPlay extends PetAction {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(ActionPlay.class);
|
||||
|
||||
public ActionPlay() {
|
||||
super(null, false);
|
||||
super(PetTasks.PLAY, false);
|
||||
this.minimumActionDuration = 4000;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Pet pet, Habbo habbo, String[] data) {
|
||||
//Play
|
||||
//TODO Implement playing for pets. For example; go to ball, toy etc.
|
||||
if (pet.getHappiness() > 75)
|
||||
pet.say(pet.getPetData().randomVocal(PetVocalsType.PLAYFUL));
|
||||
else {
|
||||
pet.say(pet.getPetData().randomVocal(PetVocalsType.DISOBEY));
|
||||
LOGGER.info("[ActionPlay] apply() called for pet: {}", pet.getName());
|
||||
|
||||
// Check if pet has enough energy to play
|
||||
if (pet.getEnergy() < 25) {
|
||||
LOGGER.info("[ActionPlay] Pet too tired, energy: {}", pet.getEnergy());
|
||||
pet.say(pet.getPetData().randomVocal(PetVocalsType.TIRED));
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if (pet.getRoom() == null || pet.getRoom().getRoomSpecialTypes() == null) {
|
||||
LOGGER.info("[ActionPlay] Room or RoomSpecialTypes is null");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get all pet toys in the room
|
||||
THashSet<InteractionPetToy> toys = pet.getRoom().getRoomSpecialTypes().getPetToys();
|
||||
LOGGER.info("[ActionPlay] Found {} pet toys in room", toys.size());
|
||||
|
||||
// Find a toy to play with
|
||||
HabboItem toy = pet.getPetData().randomToyItem(toys);
|
||||
LOGGER.info("[ActionPlay] randomToyItem returned: {}", toy != null ? toy.getId() : "null");
|
||||
|
||||
// If no compatible toy, just pick any toy in the room
|
||||
if (toy == null && !toys.isEmpty()) {
|
||||
for (InteractionPetToy t : toys) {
|
||||
toy = t;
|
||||
LOGGER.info("[ActionPlay] Using any toy: {}", toy.getId());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (toy != null) {
|
||||
RoomTile toyTile = pet.getRoom().getLayout().getTile(toy.getX(), toy.getY());
|
||||
LOGGER.info("[ActionPlay] Toy at tile: ({}, {}), tile found: {}", toy.getX(), toy.getY(), toyTile != null);
|
||||
|
||||
if (toyTile != null) {
|
||||
pet.clearPosture();
|
||||
pet.getRoomUnit().setCanWalk(true);
|
||||
pet.setTask(PetTasks.PLAY);
|
||||
|
||||
double distance = pet.getRoomUnit().getCurrentLocation().distance(toyTile);
|
||||
LOGGER.info("[ActionPlay] Distance to toy: {}", distance);
|
||||
|
||||
// Check if already at the toy
|
||||
if (distance == 0) {
|
||||
// Already at toy - start playing immediately
|
||||
LOGGER.info("[ActionPlay] Already at toy, starting play");
|
||||
this.startPlaying(pet, toy);
|
||||
} else {
|
||||
// Walk to toy first
|
||||
LOGGER.info("[ActionPlay] Setting goal location to toy");
|
||||
pet.getRoomUnit().setGoalLocation(toyTile);
|
||||
pet.say(pet.getPetData().randomVocal(PetVocalsType.PLAYFUL));
|
||||
// The InteractionPetToy.onWalkOn will handle the actual play when pet arrives
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
LOGGER.info("[ActionPlay] No toy found, doing solo play");
|
||||
// No toy found - play solo animation
|
||||
pet.clearPosture();
|
||||
pet.setTask(PetTasks.PLAY);
|
||||
pet.getRoomUnit().setStatus(RoomUnitStatus.PLAY, "0");
|
||||
pet.say(pet.getPetData().randomVocal(PetVocalsType.PLAYFUL));
|
||||
pet.packetUpdate = true;
|
||||
|
||||
// Give smaller rewards for solo play
|
||||
pet.addHappiness(10);
|
||||
pet.addEnergy(-5);
|
||||
pet.addExperience(3);
|
||||
|
||||
Emulator.getThreading().run(() -> {
|
||||
if (pet.getRoomUnit() != null) {
|
||||
pet.getRoomUnit().removeStatus(RoomUnitStatus.PLAY);
|
||||
pet.packetUpdate = true;
|
||||
}
|
||||
pet.setTask(PetTasks.FREE);
|
||||
}, this.minimumActionDuration);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void startPlaying(Pet pet, HabboItem toy) {
|
||||
pet.getRoomUnit().clearStatus();
|
||||
pet.getRoomUnit().setStatus(RoomUnitStatus.PLAY, pet.getRoomUnit().getCurrentLocation().getStackHeight() + "");
|
||||
pet.say(pet.getPetData().randomVocal(PetVocalsType.PLAYFUL));
|
||||
pet.packetUpdate = true;
|
||||
|
||||
// Playing with toy gives better rewards
|
||||
pet.addHappiness(25);
|
||||
pet.addEnergy(-10);
|
||||
pet.addExperience(10);
|
||||
|
||||
// Update toy state
|
||||
toy.setExtradata("1");
|
||||
pet.getRoom().updateItemState(toy);
|
||||
|
||||
int playDuration = 2500 + (Emulator.getRandom().nextInt(20) * 500);
|
||||
Emulator.getThreading().run(() -> {
|
||||
toy.setExtradata("0");
|
||||
pet.getRoom().updateItem(toy);
|
||||
if (pet.getRoomUnit() != null) {
|
||||
pet.getRoomUnit().removeStatus(RoomUnitStatus.PLAY);
|
||||
pet.packetUpdate = true;
|
||||
}
|
||||
pet.setTask(PetTasks.FREE);
|
||||
}, playDuration);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,9 @@ public class ActionPlayDead extends PetAction {
|
||||
|
||||
pet.getRoomUnit().setStatus(RoomUnitStatus.DEAD, pet.getRoom().getStackHeight(pet.getRoomUnit().getX(), pet.getRoomUnit().getY(), false) + "");
|
||||
|
||||
// Playing dead is not very fun
|
||||
pet.addHappiness(-3);
|
||||
|
||||
if (pet.getHappiness() > 50)
|
||||
pet.say(pet.getPetData().randomVocal(PetVocalsType.PLAYFUL));
|
||||
else
|
||||
|
||||
+38
-11
@@ -3,41 +3,68 @@ package com.eu.habbo.habbohotel.pets.actions;
|
||||
import com.eu.habbo.habbohotel.items.interactions.InteractionPushable;
|
||||
import com.eu.habbo.habbohotel.pets.Pet;
|
||||
import com.eu.habbo.habbohotel.pets.PetAction;
|
||||
import com.eu.habbo.habbohotel.pets.PetTasks;
|
||||
import com.eu.habbo.habbohotel.pets.PetVocalsType;
|
||||
import com.eu.habbo.habbohotel.rooms.Room;
|
||||
import com.eu.habbo.habbohotel.rooms.RoomTile;
|
||||
import com.eu.habbo.habbohotel.users.Habbo;
|
||||
import com.eu.habbo.habbohotel.users.HabboItem;
|
||||
|
||||
public class ActionPlayFootball extends PetAction {
|
||||
public ActionPlayFootball() {
|
||||
super(null, false);
|
||||
super(PetTasks.PLAY_FOOTBALL, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Pet pet, Habbo habbo, String[] data) {
|
||||
|
||||
Room room = pet.getRoom();
|
||||
|
||||
if(room == null || room.getLayout() == null)
|
||||
if (room == null || room.getLayout() == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pet.getRoomUnit() == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
HabboItem foundBall = null;
|
||||
// Find the nearest ball to the pet
|
||||
HabboItem nearestBall = null;
|
||||
double nearestDistance = Double.MAX_VALUE;
|
||||
RoomTile petTile = pet.getRoomUnit().getCurrentLocation();
|
||||
|
||||
for(HabboItem item : room.getFloorItems()) {
|
||||
if(item instanceof InteractionPushable) {
|
||||
foundBall = item;
|
||||
if (petTile == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (HabboItem item : room.getFloorItems()) {
|
||||
if (item instanceof InteractionPushable) {
|
||||
RoomTile ballTile = room.getLayout().getTile(item.getX(), item.getY());
|
||||
if (ballTile != null) {
|
||||
double distance = petTile.distance(ballTile);
|
||||
if (distance < nearestDistance) {
|
||||
nearestDistance = distance;
|
||||
nearestBall = item;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(foundBall == null)
|
||||
if (nearestBall == null) {
|
||||
// No ball in room - disobey
|
||||
pet.say(pet.getPetData().randomVocal(PetVocalsType.DISOBEY));
|
||||
return false;
|
||||
}
|
||||
|
||||
pet.getRoomUnit().setGoalLocation(room.getLayout().getTile(foundBall.getX(), foundBall.getY()));
|
||||
// Set task and pathfind to the ball
|
||||
pet.setTask(PetTasks.PLAY_FOOTBALL);
|
||||
pet.getRoomUnit().setCanWalk(true);
|
||||
pet.getRoomUnit().setGoalLocation(room.getLayout().getTile(nearestBall.getX(), nearestBall.getY()));
|
||||
|
||||
if (pet.getHappiness() > 75)
|
||||
if (pet.getHappiness() > 75) {
|
||||
pet.say(pet.getPetData().randomVocal(PetVocalsType.PLAYFUL));
|
||||
else
|
||||
} else {
|
||||
pet.say(pet.getPetData().randomVocal(PetVocalsType.GENERIC_NEUTRAL));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.eu.habbo.habbohotel.pets.actions;
|
||||
|
||||
import com.eu.habbo.Emulator;
|
||||
import com.eu.habbo.habbohotel.pets.Pet;
|
||||
import com.eu.habbo.habbohotel.pets.PetAction;
|
||||
import com.eu.habbo.habbohotel.pets.PetVocalsType;
|
||||
import com.eu.habbo.habbohotel.rooms.RoomUnitStatus;
|
||||
import com.eu.habbo.habbohotel.users.Habbo;
|
||||
|
||||
public class ActionRingOfFire extends PetAction {
|
||||
public ActionRingOfFire() {
|
||||
super(null, true);
|
||||
this.minimumActionDuration = 4000;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Pet pet, Habbo habbo, String[] data) {
|
||||
if (pet.getRoom() == null || pet.getRoomUnit() == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Ring of Fire can only be performed while hanging on a tree
|
||||
if (!pet.getRoomUnit().hasStatus(RoomUnitStatus.HANG)) {
|
||||
pet.say(pet.getPetData().randomVocal(PetVocalsType.DISOBEY));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Transition from HANG to RINGOFFIRE
|
||||
pet.getRoomUnit().removeStatus(RoomUnitStatus.HANG);
|
||||
pet.getRoomUnit().setStatus(RoomUnitStatus.RINGOFFIRE, pet.getRoomUnit().getCurrentLocation().getStackHeight() + "");
|
||||
pet.packetUpdate = true;
|
||||
|
||||
// After ring of fire, go back to hanging
|
||||
Emulator.getThreading().run(() -> {
|
||||
pet.getRoomUnit().removeStatus(RoomUnitStatus.RINGOFFIRE);
|
||||
pet.getRoomUnit().setStatus(RoomUnitStatus.HANG, "");
|
||||
pet.packetUpdate = true;
|
||||
}, minimumActionDuration);
|
||||
|
||||
pet.say(pet.getPetData().randomVocal(PetVocalsType.PLAYFUL));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.eu.habbo.habbohotel.pets.actions;
|
||||
|
||||
import com.eu.habbo.Emulator;
|
||||
import com.eu.habbo.habbohotel.pets.Pet;
|
||||
import com.eu.habbo.habbohotel.pets.PetAction;
|
||||
import com.eu.habbo.habbohotel.pets.PetTasks;
|
||||
import com.eu.habbo.habbohotel.pets.PetVocalsType;
|
||||
import com.eu.habbo.habbohotel.rooms.RoomUnitStatus;
|
||||
import com.eu.habbo.habbohotel.users.Habbo;
|
||||
import com.eu.habbo.threading.runnables.PetClearPosture;
|
||||
|
||||
public class ActionRoll extends PetAction {
|
||||
public ActionRoll() {
|
||||
super(PetTasks.FREE, true);
|
||||
this.minimumActionDuration = 3000;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Pet pet, Habbo habbo, String[] data) {
|
||||
pet.clearPosture();
|
||||
pet.getRoomUnit().setStatus(RoomUnitStatus.LAY, pet.getRoomUnit().getCurrentLocation().getStackHeight() + "");
|
||||
|
||||
Emulator.getThreading().run(new PetClearPosture(pet, RoomUnitStatus.LAY, null, false), 3000);
|
||||
|
||||
if (pet.getHappiness() > 50)
|
||||
pet.say(pet.getPetData().randomVocal(PetVocalsType.PLAYFUL));
|
||||
else
|
||||
pet.say(pet.getPetData().randomVocal(PetVocalsType.GENERIC_NEUTRAL));
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.eu.habbo.habbohotel.pets.actions;
|
||||
|
||||
import com.eu.habbo.Emulator;
|
||||
import com.eu.habbo.habbohotel.pets.Pet;
|
||||
import com.eu.habbo.habbohotel.pets.PetAction;
|
||||
import com.eu.habbo.habbohotel.pets.PetTasks;
|
||||
@@ -10,12 +11,26 @@ import com.eu.habbo.habbohotel.users.Habbo;
|
||||
public class ActionSit extends PetAction {
|
||||
public ActionSit() {
|
||||
super(PetTasks.SIT, true);
|
||||
this.minimumActionDuration = 4000;
|
||||
this.statusToRemove.add(RoomUnitStatus.BEG);
|
||||
this.statusToRemove.add(RoomUnitStatus.MOVE);
|
||||
this.statusToRemove.add(RoomUnitStatus.LAY);
|
||||
this.statusToRemove.add(RoomUnitStatus.DEAD);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Pet pet, Habbo habbo, String[] data) {
|
||||
if (pet.getTask() != PetTasks.SIT && !pet.getRoomUnit().hasStatus(RoomUnitStatus.SIT)) {
|
||||
pet.getRoomUnit().setStatus(RoomUnitStatus.SIT, pet.getRoom().getStackHeight(pet.getRoomUnit().getX(), pet.getRoomUnit().getY(), false) - 0.50 + "");
|
||||
pet.getRoomUnit().cmdSit = true;
|
||||
pet.getRoomUnit().setStatus(RoomUnitStatus.SIT, pet.getRoomUnit().getCurrentLocation().getStackHeight() + "");
|
||||
|
||||
// Sitting is a bit boring
|
||||
pet.addHappiness(-2);
|
||||
|
||||
Emulator.getThreading().run(() -> {
|
||||
pet.getRoomUnit().cmdSit = false;
|
||||
pet.clearPosture();
|
||||
}, this.minimumActionDuration);
|
||||
|
||||
if (pet.getHappiness() > 75)
|
||||
pet.say(pet.getPetData().randomVocal(PetVocalsType.PLAYFUL));
|
||||
@@ -25,6 +40,6 @@ public class ActionSit extends PetAction {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,9 @@ public class ActionSpeak extends PetAction {
|
||||
pet.setMuted(false);
|
||||
Emulator.getThreading().run(new PetClearPosture(pet, RoomUnitStatus.SPEAK, null, false), 2000);
|
||||
|
||||
// Speaking/expressing itself makes pet happy
|
||||
pet.addHappiness(3);
|
||||
|
||||
if (pet.getHappiness() > 70)
|
||||
pet.say(pet.getPetData().randomVocal(PetVocalsType.GENERIC_HAPPY));
|
||||
else if (pet.getHappiness() < 30)
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
package com.eu.habbo.habbohotel.pets.actions;
|
||||
|
||||
import com.eu.habbo.Emulator;
|
||||
import com.eu.habbo.habbohotel.pets.Pet;
|
||||
import com.eu.habbo.habbohotel.pets.PetAction;
|
||||
import com.eu.habbo.habbohotel.pets.PetTasks;
|
||||
import com.eu.habbo.habbohotel.pets.PetVocalsType;
|
||||
import com.eu.habbo.habbohotel.rooms.RoomUserRotation;
|
||||
import com.eu.habbo.habbohotel.users.Habbo;
|
||||
|
||||
public class ActionSpin extends PetAction {
|
||||
public ActionSpin() {
|
||||
super(PetTasks.FREE, true);
|
||||
this.minimumActionDuration = 2000;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Pet pet, Habbo habbo, String[] data) {
|
||||
pet.clearPosture();
|
||||
|
||||
// Spin animation - rotate through all directions
|
||||
for (int i = 0; i < 8; i++) {
|
||||
final int rotation = i;
|
||||
Emulator.getThreading().run(() -> {
|
||||
pet.getRoomUnit().setRotation(RoomUserRotation.values()[rotation]);
|
||||
pet.packetUpdate = true;
|
||||
}, i * 250);
|
||||
}
|
||||
|
||||
if (pet.getHappiness() > 50)
|
||||
pet.say(pet.getPetData().randomVocal(PetVocalsType.PLAYFUL));
|
||||
else
|
||||
pet.say(pet.getPetData().randomVocal(PetVocalsType.GENERIC_NEUTRAL));
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,10 @@ public class ActionStay extends PetAction {
|
||||
|
||||
pet.getRoomUnit().setCanWalk(false);
|
||||
pet.setStayStartedAt(Emulator.getIntUnixTimestamp());
|
||||
|
||||
// Staying still is boring
|
||||
pet.addHappiness(-5);
|
||||
|
||||
pet.say(pet.getPetData().randomVocal(PetVocalsType.GENERIC_NEUTRAL));
|
||||
|
||||
return true;
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
package com.eu.habbo.habbohotel.pets.actions;
|
||||
|
||||
import com.eu.habbo.Emulator;
|
||||
import com.eu.habbo.habbohotel.items.interactions.pets.InteractionPetTree;
|
||||
import com.eu.habbo.habbohotel.pets.Pet;
|
||||
import com.eu.habbo.habbohotel.pets.PetAction;
|
||||
import com.eu.habbo.habbohotel.pets.PetTasks;
|
||||
import com.eu.habbo.habbohotel.pets.PetVocalsType;
|
||||
import com.eu.habbo.habbohotel.rooms.RoomUnitStatus;
|
||||
import com.eu.habbo.habbohotel.users.Habbo;
|
||||
import com.eu.habbo.habbohotel.users.HabboItem;
|
||||
import com.eu.habbo.threading.runnables.PetClearPosture;
|
||||
|
||||
public class ActionSwing extends PetAction {
|
||||
public ActionSwing() {
|
||||
super(PetTasks.FREE, true);
|
||||
this.minimumActionDuration = 4000;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Pet pet, Habbo habbo, String[] data) {
|
||||
// Swing requires pet to be on a pet tree (dragon/monkey tree)
|
||||
if (pet.getRoom() == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
HabboItem itemBelow = pet.getRoom().getTopItemAt(pet.getRoomUnit().getX(), pet.getRoomUnit().getY());
|
||||
if (!(itemBelow instanceof InteractionPetTree)) {
|
||||
// Pet must go to tree first
|
||||
pet.findTree();
|
||||
pet.say(pet.getPetData().randomVocal(PetVocalsType.DISOBEY));
|
||||
return false;
|
||||
}
|
||||
|
||||
pet.clearPosture();
|
||||
pet.getRoomUnit().setStatus(RoomUnitStatus.SWING, "");
|
||||
|
||||
Emulator.getThreading().run(new PetClearPosture(pet, RoomUnitStatus.SWING, null, false), 4000);
|
||||
|
||||
if (pet.getHappiness() > 50)
|
||||
pet.say(pet.getPetData().randomVocal(PetVocalsType.PLAYFUL));
|
||||
else
|
||||
pet.say(pet.getPetData().randomVocal(PetVocalsType.GENERIC_NEUTRAL));
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.eu.habbo.habbohotel.pets.actions;
|
||||
|
||||
import com.eu.habbo.habbohotel.pets.Pet;
|
||||
import com.eu.habbo.habbohotel.pets.PetAction;
|
||||
import com.eu.habbo.habbohotel.pets.PetTasks;
|
||||
import com.eu.habbo.habbohotel.pets.PetVocalsType;
|
||||
import com.eu.habbo.habbohotel.users.Habbo;
|
||||
|
||||
public class ActionSwitch extends PetAction {
|
||||
public ActionSwitch() {
|
||||
super(PetTasks.FREE, true);
|
||||
this.minimumActionDuration = 1000;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Pet pet, Habbo habbo, String[] data) {
|
||||
pet.clearPosture();
|
||||
|
||||
// Switch/toggle behavior - pet acknowledges command
|
||||
if (pet.getHappiness() > 50)
|
||||
pet.say(pet.getPetData().randomVocal(PetVocalsType.PLAYFUL));
|
||||
else
|
||||
pet.say(pet.getPetData().randomVocal(PetVocalsType.GENERIC_NEUTRAL));
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.eu.habbo.habbohotel.pets.actions;
|
||||
|
||||
import com.eu.habbo.habbohotel.pets.Pet;
|
||||
import com.eu.habbo.habbohotel.pets.PetAction;
|
||||
import com.eu.habbo.habbohotel.pets.PetTasks;
|
||||
import com.eu.habbo.habbohotel.pets.PetVocalsType;
|
||||
import com.eu.habbo.habbohotel.rooms.RoomTile;
|
||||
import com.eu.habbo.habbohotel.users.Habbo;
|
||||
|
||||
public class ActionTeleport extends PetAction {
|
||||
public ActionTeleport() {
|
||||
super(PetTasks.FREE, true);
|
||||
this.minimumActionDuration = 1000;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Pet pet, Habbo habbo, String[] data) {
|
||||
// Teleport pet to a random walkable tile near owner
|
||||
if (habbo != null && habbo.getRoomUnit() != null && pet.getRoom() != null) {
|
||||
RoomTile targetTile = pet.getRoom().getLayout().getTileInFront(
|
||||
habbo.getRoomUnit().getCurrentLocation(),
|
||||
habbo.getRoomUnit().getBodyRotation().getValue()
|
||||
);
|
||||
|
||||
if (targetTile != null && targetTile.isWalkable()) {
|
||||
pet.getRoomUnit().setLocation(targetTile);
|
||||
pet.getRoomUnit().setZ(targetTile.getStackHeight());
|
||||
pet.packetUpdate = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (pet.getHappiness() > 50)
|
||||
pet.say(pet.getPetData().randomVocal(PetVocalsType.PLAYFUL));
|
||||
else
|
||||
pet.say(pet.getPetData().randomVocal(PetVocalsType.GENERIC_NEUTRAL));
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,7 @@ public class ActionTorch extends PetAction {
|
||||
return false;
|
||||
}
|
||||
|
||||
pet.say(pet.getPetData().randomVocal(PetVocalsType.PLAYFUL));
|
||||
Emulator.getThreading().run(new PetClearPosture(pet, RoomUnitStatus.EAT, null, false), this.minimumActionDuration);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.eu.habbo.habbohotel.pets.actions;
|
||||
|
||||
import com.eu.habbo.Emulator;
|
||||
import com.eu.habbo.habbohotel.pets.Pet;
|
||||
import com.eu.habbo.habbohotel.pets.PetAction;
|
||||
import com.eu.habbo.habbohotel.pets.PetTasks;
|
||||
import com.eu.habbo.habbohotel.pets.PetVocalsType;
|
||||
import com.eu.habbo.habbohotel.rooms.RoomUnitStatus;
|
||||
import com.eu.habbo.habbohotel.users.Habbo;
|
||||
import com.eu.habbo.threading.runnables.PetClearPosture;
|
||||
|
||||
public class ActionTripleJump extends PetAction {
|
||||
public ActionTripleJump() {
|
||||
super(PetTasks.JUMP, true);
|
||||
this.minimumActionDuration = 4000;
|
||||
this.statusToSet.add(RoomUnitStatus.JUMP);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Pet pet, Habbo habbo, String[] data) {
|
||||
pet.clearPosture();
|
||||
|
||||
// Triple jump - three jump animations
|
||||
for (int i = 0; i < 3; i++) {
|
||||
Emulator.getThreading().run(() -> {
|
||||
pet.getRoomUnit().setStatus(RoomUnitStatus.JUMP, "");
|
||||
pet.packetUpdate = true;
|
||||
}, i * 1200);
|
||||
}
|
||||
|
||||
Emulator.getThreading().run(new PetClearPosture(pet, RoomUnitStatus.JUMP, null, false), 4000);
|
||||
|
||||
if (pet.getHappiness() > 70)
|
||||
pet.say(pet.getPetData().randomVocal(PetVocalsType.PLAYFUL));
|
||||
else
|
||||
pet.say(pet.getPetData().randomVocal(PetVocalsType.GENERIC_NEUTRAL));
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.eu.habbo.habbohotel.pets.actions;
|
||||
|
||||
import com.eu.habbo.Emulator;
|
||||
import com.eu.habbo.habbohotel.pets.Pet;
|
||||
import com.eu.habbo.habbohotel.pets.PetAction;
|
||||
import com.eu.habbo.habbohotel.pets.PetTasks;
|
||||
import com.eu.habbo.habbohotel.pets.PetVocalsType;
|
||||
import com.eu.habbo.habbohotel.rooms.RoomUnitStatus;
|
||||
import com.eu.habbo.habbohotel.users.Habbo;
|
||||
import com.eu.habbo.threading.runnables.PetClearPosture;
|
||||
|
||||
public class ActionWagTail extends PetAction {
|
||||
public ActionWagTail() {
|
||||
super(PetTasks.FREE, true);
|
||||
this.minimumActionDuration = 2000;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Pet pet, Habbo habbo, String[] data) {
|
||||
pet.clearPosture();
|
||||
pet.getRoomUnit().setStatus(RoomUnitStatus.WAG_TAIL, "");
|
||||
|
||||
Emulator.getThreading().run(new PetClearPosture(pet, RoomUnitStatus.WAG_TAIL, null, false), 2000);
|
||||
|
||||
if (pet.getHappiness() > 40)
|
||||
pet.say(pet.getPetData().randomVocal(PetVocalsType.GENERIC_HAPPY));
|
||||
else
|
||||
pet.say(pet.getPetData().randomVocal(PetVocalsType.GENERIC_NEUTRAL));
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import com.eu.habbo.Emulator;
|
||||
import com.eu.habbo.habbohotel.pets.Pet;
|
||||
import com.eu.habbo.habbohotel.pets.PetAction;
|
||||
import com.eu.habbo.habbohotel.pets.PetTasks;
|
||||
import com.eu.habbo.habbohotel.pets.PetVocalsType;
|
||||
import com.eu.habbo.habbohotel.rooms.RoomUnitStatus;
|
||||
import com.eu.habbo.habbohotel.users.Habbo;
|
||||
import com.eu.habbo.threading.runnables.PetClearPosture;
|
||||
@@ -22,9 +23,15 @@ public class ActionWave extends PetAction {
|
||||
pet.getRoomUnit().setStatus(RoomUnitStatus.WAVE, "0");
|
||||
|
||||
Emulator.getThreading().run(new PetClearPosture(pet, RoomUnitStatus.WAVE, null, false), 2000);
|
||||
|
||||
// Waving is a fun trick
|
||||
pet.addHappiness(5);
|
||||
|
||||
pet.say(pet.getPetData().randomVocal(PetVocalsType.PLAYFUL));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
pet.say(pet.getPetData().randomVocal(PetVocalsType.GENERIC_NEUTRAL));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,175 @@
|
||||
package com.eu.habbo.habbohotel.pets.breeding;
|
||||
|
||||
import com.eu.habbo.Emulator;
|
||||
import com.eu.habbo.habbohotel.items.interactions.pets.InteractionPetBreedingNest;
|
||||
import com.eu.habbo.habbohotel.pets.Pet;
|
||||
import com.eu.habbo.habbohotel.pets.PetTasks;
|
||||
import com.eu.habbo.habbohotel.users.Habbo;
|
||||
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Represents an active breeding session between two pets.
|
||||
* Manages the state and lifecycle of the breeding process.
|
||||
*/
|
||||
public class PetBreedingSession {
|
||||
private final InteractionPetBreedingNest nest;
|
||||
private final Pet petOne;
|
||||
private Pet petTwo;
|
||||
private final long startTime;
|
||||
private BreedingState state;
|
||||
private ScheduledFuture<?> timeoutTask;
|
||||
|
||||
/**
|
||||
* Represents the various states of a breeding session.
|
||||
*/
|
||||
public enum BreedingState {
|
||||
WAITING_FOR_SECOND_PET,
|
||||
WAITING_FOR_CONFIRMATION,
|
||||
BREEDING_IN_PROGRESS,
|
||||
COMPLETED,
|
||||
CANCELLED
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new breeding session with the first pet.
|
||||
* @param nest The breeding nest item
|
||||
* @param firstPet The first pet to enter the nest
|
||||
*/
|
||||
public PetBreedingSession(InteractionPetBreedingNest nest, Pet firstPet) {
|
||||
this.nest = nest;
|
||||
this.petOne = firstPet;
|
||||
this.petTwo = null;
|
||||
this.startTime = System.currentTimeMillis();
|
||||
this.state = BreedingState.WAITING_FOR_SECOND_PET;
|
||||
|
||||
// Auto-cancel if second pet doesn't arrive within configured timeout
|
||||
int timeoutSeconds = Emulator.getConfig().getInt("pet.breeding.timeout_seconds", 120);
|
||||
this.timeoutTask = Emulator.getThreading().getService().schedule(() -> {
|
||||
if (this.state == BreedingState.WAITING_FOR_SECOND_PET) {
|
||||
this.cancel("Timeout waiting for second pet");
|
||||
}
|
||||
}, timeoutSeconds, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to add the second pet to the breeding session.
|
||||
* @param pet The second pet to add
|
||||
* @return true if the pet was successfully added
|
||||
*/
|
||||
public boolean addSecondPet(Pet pet) {
|
||||
if (this.state != BreedingState.WAITING_FOR_SECOND_PET) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Validate compatibility - must be same pet type
|
||||
if (pet.getPetData().getType() != this.petOne.getPetData().getType()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if breeding is possible for this pet type
|
||||
if (pet.getPetData().getOffspringType() == -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Don't allow breeding with self
|
||||
if (pet.getId() == this.petOne.getId()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.petTwo = pet;
|
||||
this.state = BreedingState.WAITING_FOR_CONFIRMATION;
|
||||
|
||||
// Cancel the timeout task since we have both pets
|
||||
if (this.timeoutTask != null && !this.timeoutTask.isDone()) {
|
||||
this.timeoutTask.cancel(false);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirms the breeding and starts the process.
|
||||
* @param habbo The habbo confirming the breeding
|
||||
* @param offspringName The name for the offspring
|
||||
*/
|
||||
public void confirm(Habbo habbo, String offspringName) {
|
||||
if (this.state != BreedingState.WAITING_FOR_CONFIRMATION) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.state = BreedingState.BREEDING_IN_PROGRESS;
|
||||
this.nest.breed(habbo, offspringName, this.petOne.getId(), this.petTwo.getId());
|
||||
this.state = BreedingState.COMPLETED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels the breeding session and releases the pets.
|
||||
* @param reason The reason for cancellation
|
||||
*/
|
||||
public void cancel(String reason) {
|
||||
if (this.state == BreedingState.COMPLETED || this.state == BreedingState.CANCELLED) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.state = BreedingState.CANCELLED;
|
||||
|
||||
// Release first pet
|
||||
if (this.petOne != null && this.petOne.getRoomUnit() != null) {
|
||||
this.petOne.getRoomUnit().setCanWalk(true);
|
||||
this.petOne.setTask(PetTasks.FREE);
|
||||
}
|
||||
|
||||
// Release second pet
|
||||
if (this.petTwo != null && this.petTwo.getRoomUnit() != null) {
|
||||
this.petTwo.getRoomUnit().setCanWalk(true);
|
||||
this.petTwo.setTask(PetTasks.FREE);
|
||||
}
|
||||
|
||||
// Reset nest state
|
||||
this.nest.setExtradata("0");
|
||||
if (this.nest.getRoomId() > 0) {
|
||||
com.eu.habbo.habbohotel.rooms.Room room = com.eu.habbo.Emulator.getGameEnvironment().getRoomManager().getRoom(this.nest.getRoomId());
|
||||
if (room != null) {
|
||||
room.updateItem(this.nest);
|
||||
}
|
||||
}
|
||||
|
||||
// Cancel any pending timeout task
|
||||
if (this.timeoutTask != null && !this.timeoutTask.isDone()) {
|
||||
this.timeoutTask.cancel(false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the breeding session is still valid.
|
||||
* @return true if both pets are still in the same room
|
||||
*/
|
||||
public boolean isValid() {
|
||||
if (this.petOne == null || this.petOne.getRoom() == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.petTwo != null && this.petTwo.getRoom() != this.petOne.getRoom()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Getters
|
||||
public InteractionPetBreedingNest getNest() { return nest; }
|
||||
public Pet getPetOne() { return petOne; }
|
||||
public Pet getPetTwo() { return petTwo; }
|
||||
public long getStartTime() { return startTime; }
|
||||
public BreedingState getState() { return state; }
|
||||
|
||||
/**
|
||||
* Gets how long the session has been active in milliseconds.
|
||||
* @return Duration in milliseconds
|
||||
*/
|
||||
public long getDuration() {
|
||||
return System.currentTimeMillis() - this.startTime;
|
||||
}
|
||||
}
|
||||
@@ -946,8 +946,20 @@ public class Room implements Comparable<Room>, ISerialize, Runnable {
|
||||
}
|
||||
}
|
||||
|
||||
this.unitManager.clear();
|
||||
// Save ALL remaining pets (including owner's pets) BEFORE clearing
|
||||
TIntObjectIterator<Pet> petIterator = this.getCurrentPets().iterator();
|
||||
for (int i = this.getCurrentPets().size(); i-- > 0; ) {
|
||||
try {
|
||||
petIterator.advance();
|
||||
petIterator.value().needsUpdate = true;
|
||||
petIterator.value().run(); // Run synchronously to ensure DB is updated before room reload
|
||||
} catch (NoSuchElementException e) {
|
||||
LOGGER.error("Caught exception", e);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
this.unitManager.clear();
|
||||
this.unitManager.clearBots();
|
||||
this.unitManager.clearPets();
|
||||
} catch (Exception e) {
|
||||
|
||||
@@ -434,6 +434,12 @@ public class RoomCycleManager {
|
||||
}
|
||||
|
||||
if (!unit.isWalking() && !unit.cmdSit) {
|
||||
// Don't override special pet statuses with SIT
|
||||
boolean hasSpecialPetStatus = unit.hasStatus(RoomUnitStatus.HANG)
|
||||
|| unit.hasStatus(RoomUnitStatus.SWING)
|
||||
|| unit.hasStatus(RoomUnitStatus.FLAME)
|
||||
|| unit.hasStatus(RoomUnitStatus.PLAY);
|
||||
|
||||
RoomTile thisTile = this.room.getLayout().getTile(unit.getX(), unit.getY());
|
||||
HabboItem topItem = this.room.getTallestChair(thisTile);
|
||||
|
||||
@@ -442,7 +448,7 @@ public class RoomCycleManager {
|
||||
unit.removeStatus(RoomUnitStatus.SIT);
|
||||
update = true;
|
||||
}
|
||||
} else if (thisTile.state == RoomTileState.SIT && (!unit.hasStatus(RoomUnitStatus.SIT)
|
||||
} else if (!hasSpecialPetStatus && thisTile.state == RoomTileState.SIT && (!unit.hasStatus(RoomUnitStatus.SIT)
|
||||
|| unit.sitUpdate)) {
|
||||
this.room.dance(unit, DanceType.NONE);
|
||||
unit.setStatus(RoomUnitStatus.SIT, (Item.getCurrentHeight(topItem) * 1.0D) + "");
|
||||
|
||||
@@ -13,11 +13,9 @@ import com.eu.habbo.habbohotel.items.interactions.games.battlebanzai.Interaction
|
||||
import com.eu.habbo.habbohotel.items.interactions.games.freeze.InteractionFreezeExitTile;
|
||||
import com.eu.habbo.habbohotel.items.interactions.games.tag.InteractionTagField;
|
||||
import com.eu.habbo.habbohotel.items.interactions.games.tag.InteractionTagPole;
|
||||
import com.eu.habbo.habbohotel.items.interactions.pets.InteractionNest;
|
||||
import com.eu.habbo.habbohotel.items.interactions.pets.InteractionPetBreedingNest;
|
||||
import com.eu.habbo.habbohotel.items.interactions.pets.InteractionPetDrink;
|
||||
import com.eu.habbo.habbohotel.items.interactions.pets.InteractionPetFood;
|
||||
import com.eu.habbo.habbohotel.items.interactions.pets.*;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredBlob;
|
||||
import com.eu.habbo.habbohotel.permissions.Permission;
|
||||
import com.eu.habbo.habbohotel.users.Habbo;
|
||||
import com.eu.habbo.habbohotel.users.HabboInfo;
|
||||
import com.eu.habbo.habbohotel.users.HabboItem;
|
||||
@@ -25,23 +23,10 @@ import com.eu.habbo.habbohotel.users.HabboManager;
|
||||
import com.eu.habbo.habbohotel.wired.core.WiredManager;
|
||||
import com.eu.habbo.habbohotel.wired.tick.WiredTickable;
|
||||
import com.eu.habbo.messages.outgoing.inventory.AddHabboItemComposer;
|
||||
import com.eu.habbo.messages.outgoing.rooms.items.FloorItemUpdateComposer;
|
||||
import com.eu.habbo.messages.outgoing.rooms.items.ItemStateComposer;
|
||||
import com.eu.habbo.messages.outgoing.rooms.items.RemoveFloorItemComposer;
|
||||
import com.eu.habbo.messages.outgoing.rooms.items.RemoveWallItemComposer;
|
||||
import com.eu.habbo.messages.outgoing.rooms.items.WallItemUpdateComposer;
|
||||
import com.eu.habbo.habbohotel.permissions.Permission;
|
||||
import com.eu.habbo.messages.outgoing.rooms.items.AddFloorItemComposer;
|
||||
import com.eu.habbo.messages.outgoing.rooms.items.AddWallItemComposer;
|
||||
import com.eu.habbo.messages.outgoing.rooms.items.FloorItemOnRollerComposer;
|
||||
import com.eu.habbo.messages.outgoing.rooms.items.*;
|
||||
import com.eu.habbo.plugin.Event;
|
||||
import com.eu.habbo.plugin.events.furniture.FurnitureBuildheightEvent;
|
||||
import com.eu.habbo.plugin.events.furniture.FurnitureMovedEvent;
|
||||
import com.eu.habbo.plugin.events.furniture.FurniturePickedUpEvent;
|
||||
import com.eu.habbo.plugin.events.furniture.FurniturePlacedEvent;
|
||||
import com.eu.habbo.plugin.events.furniture.FurnitureRotatedEvent;
|
||||
import com.eu.habbo.plugin.events.furniture.*;
|
||||
import gnu.trove.TCollections;
|
||||
import org.apache.commons.math3.util.Pair;
|
||||
import gnu.trove.iterator.TIntObjectIterator;
|
||||
import gnu.trove.map.TIntIntMap;
|
||||
import gnu.trove.map.TIntObjectMap;
|
||||
@@ -49,6 +34,7 @@ import gnu.trove.map.hash.THashMap;
|
||||
import gnu.trove.map.hash.TIntIntHashMap;
|
||||
import gnu.trove.map.hash.TIntObjectHashMap;
|
||||
import gnu.trove.set.hash.THashSet;
|
||||
import org.apache.commons.math3.util.Pair;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -661,6 +647,10 @@ public class RoomItemManager {
|
||||
specialTypes.addPetDrink((InteractionPetDrink) item);
|
||||
} else if (item instanceof InteractionPetFood) {
|
||||
specialTypes.addPetFood((InteractionPetFood) item);
|
||||
} else if (item instanceof InteractionPetToy) {
|
||||
specialTypes.addPetToy((InteractionPetToy) item);
|
||||
} else if (item instanceof InteractionPetTree) {
|
||||
specialTypes.addPetTree((InteractionPetTree) item);
|
||||
} else if (item instanceof InteractionMoodLight ||
|
||||
item instanceof InteractionPyramid ||
|
||||
item instanceof InteractionMusicDisc ||
|
||||
@@ -780,6 +770,10 @@ public class RoomItemManager {
|
||||
specialTypes.removePetDrink((InteractionPetDrink) item);
|
||||
} else if (item instanceof InteractionPetFood) {
|
||||
specialTypes.removePetFood((InteractionPetFood) item);
|
||||
} else if (item instanceof InteractionPetToy) {
|
||||
specialTypes.removePetToy((InteractionPetToy) item);
|
||||
} else if (item instanceof InteractionPetTree) {
|
||||
specialTypes.removePetTree((InteractionPetTree) item);
|
||||
} else if (item instanceof InteractionMoodLight ||
|
||||
item instanceof InteractionPyramid ||
|
||||
item instanceof InteractionMusicDisc ||
|
||||
|
||||
@@ -19,6 +19,7 @@ import com.eu.habbo.habbohotel.items.interactions.pets.InteractionNest;
|
||||
import com.eu.habbo.habbohotel.items.interactions.pets.InteractionPetDrink;
|
||||
import com.eu.habbo.habbohotel.items.interactions.pets.InteractionPetFood;
|
||||
import com.eu.habbo.habbohotel.items.interactions.pets.InteractionPetToy;
|
||||
import com.eu.habbo.habbohotel.items.interactions.pets.InteractionPetTree;
|
||||
import com.eu.habbo.habbohotel.users.HabboItem;
|
||||
import com.eu.habbo.habbohotel.wired.WiredConditionType;
|
||||
import com.eu.habbo.habbohotel.wired.WiredEffectType;
|
||||
@@ -45,6 +46,7 @@ public class RoomSpecialTypes {
|
||||
private final THashMap<Integer, InteractionPetDrink> petDrinks;
|
||||
private final THashMap<Integer, InteractionPetFood> petFoods;
|
||||
private final THashMap<Integer, InteractionPetToy> petToys;
|
||||
private final THashMap<Integer, InteractionPetTree> petTrees;
|
||||
private final THashMap<Integer, InteractionRoller> rollers;
|
||||
|
||||
// Thread-safe wired collections using ConcurrentHashMap for better concurrency
|
||||
@@ -73,6 +75,7 @@ public class RoomSpecialTypes {
|
||||
this.petDrinks = new THashMap<>(0);
|
||||
this.petFoods = new THashMap<>(0);
|
||||
this.petToys = new THashMap<>(0);
|
||||
this.petTrees = new THashMap<>(0);
|
||||
this.rollers = new THashMap<>(0);
|
||||
|
||||
this.wiredTriggers = new ConcurrentHashMap<>();
|
||||
@@ -232,6 +235,28 @@ public class RoomSpecialTypes {
|
||||
}
|
||||
|
||||
|
||||
public InteractionPetTree getPetTree(int itemId) {
|
||||
return this.petTrees.get(itemId);
|
||||
}
|
||||
|
||||
public void addPetTree(InteractionPetTree item) {
|
||||
this.petTrees.put(item.getId(), item);
|
||||
}
|
||||
|
||||
public void removePetTree(InteractionPetTree petTree) {
|
||||
this.petTrees.remove(petTree.getId());
|
||||
}
|
||||
|
||||
public THashSet<InteractionPetTree> getPetTrees() {
|
||||
synchronized (this.petTrees) {
|
||||
THashSet<InteractionPetTree> petTrees = new THashSet<>();
|
||||
petTrees.addAll(this.petTrees.values());
|
||||
|
||||
return petTrees;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public InteractionRoller getRoller(int itemId) {
|
||||
synchronized (this.rollers) {
|
||||
return this.rollers.get(itemId);
|
||||
@@ -913,6 +938,23 @@ public class RoomSpecialTypes {
|
||||
|
||||
public THashSet<HabboItem> getItemsOfType(Class<? extends HabboItem> type) {
|
||||
THashSet<HabboItem> items = new THashSet<>();
|
||||
|
||||
// Check pet trees collection for InteractionPetTree type
|
||||
if (type == InteractionPetTree.class) {
|
||||
synchronized (this.petTrees) {
|
||||
items.addAll(this.petTrees.values());
|
||||
}
|
||||
return items;
|
||||
}
|
||||
|
||||
// Check pet toys collection for InteractionPetToy type
|
||||
if (type == InteractionPetToy.class) {
|
||||
synchronized (this.petToys) {
|
||||
items.addAll(this.petToys.values());
|
||||
}
|
||||
return items;
|
||||
}
|
||||
|
||||
synchronized (this.undefined) {
|
||||
for (HabboItem item : this.undefined.values()) {
|
||||
if (item.getClass() == type)
|
||||
@@ -959,6 +1001,8 @@ public class RoomSpecialTypes {
|
||||
this.nests.clear();
|
||||
this.petDrinks.clear();
|
||||
this.petFoods.clear();
|
||||
this.petToys.clear();
|
||||
this.petTrees.clear();
|
||||
this.rollers.clear();
|
||||
|
||||
this.wiredTriggers.clear();
|
||||
|
||||
@@ -6,6 +6,7 @@ import com.eu.habbo.habbohotel.bots.VisitorBot;
|
||||
import com.eu.habbo.habbohotel.items.Item;
|
||||
import com.eu.habbo.habbohotel.pets.Pet;
|
||||
import com.eu.habbo.habbohotel.pets.PetManager;
|
||||
import com.eu.habbo.habbohotel.pets.PetVocalsType;
|
||||
import com.eu.habbo.habbohotel.pets.RideablePet;
|
||||
import com.eu.habbo.habbohotel.users.DanceType;
|
||||
import com.eu.habbo.habbohotel.users.Habbo;
|
||||
@@ -983,7 +984,7 @@ public class RoomUnitManager {
|
||||
((RideablePet) pet).setRider(null);
|
||||
}
|
||||
|
||||
Emulator.getThreading().run(pet);
|
||||
pet.run(); // Run synchronously to ensure DB is updated before returning pet to inventory
|
||||
habbo.getInventory().getPetsComponent().addPet(pet);
|
||||
habbo.getClient().sendResponse(new AddPetComposer(pet));
|
||||
this.currentPets.remove(pet.getId());
|
||||
@@ -1266,11 +1267,29 @@ public class RoomUnitManager {
|
||||
// ==================== VISITOR BOT HANDLING ====================
|
||||
|
||||
/**
|
||||
* Handles Habbo entering the room (visitor bot notification).
|
||||
* Handles Habbo entering the room (visitor bot notification and pet greeting).
|
||||
*/
|
||||
public void habboEntered(Habbo habbo) {
|
||||
habbo.getRoomUnit().animateWalk = false;
|
||||
|
||||
// Have pets greet their owner
|
||||
synchronized (this.currentPets) {
|
||||
TIntObjectIterator<Pet> petIterator = this.currentPets.iterator();
|
||||
for (int i = this.currentPets.size(); i-- > 0; ) {
|
||||
try {
|
||||
petIterator.advance();
|
||||
Pet pet = petIterator.value();
|
||||
if (pet.getUserId() == habbo.getHabboInfo().getId()) {
|
||||
// Pet sees its owner - greet them!
|
||||
pet.say(pet.getPetData().randomVocal(PetVocalsType.GREET_OWNER));
|
||||
pet.addHappiness(10);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
synchronized (this.currentBots) {
|
||||
if (habbo.getHabboInfo().getId() != this.room.getOwnerId()) {
|
||||
return;
|
||||
|
||||
@@ -55,6 +55,11 @@ public enum RoomUnitStatus {
|
||||
KICK("kck"),
|
||||
WAG_TAIL("wag"),
|
||||
DANCE("dan"),
|
||||
RINGOFFIRE("rng"),
|
||||
SWING("swg"),
|
||||
HANG("hg"),
|
||||
ROLL("rll"),
|
||||
FLAT("flt"),
|
||||
AMS("ams"),
|
||||
SWIM("swm"),
|
||||
TURN("trn"),
|
||||
|
||||
+1
-1
@@ -37,7 +37,7 @@ public class PetPickupEvent extends MessageHandler {
|
||||
}
|
||||
|
||||
pet.removeFromRoom();
|
||||
Emulator.getThreading().run(pet);
|
||||
pet.run(); // Run synchronously to ensure DB is updated before returning pet to inventory
|
||||
|
||||
if (this.client.getHabbo().getHabboInfo().getId() == pet.getUserId()) {
|
||||
this.client.sendResponse(new AddPetComposer(pet));
|
||||
|
||||
@@ -6,6 +6,7 @@ import com.eu.habbo.habbohotel.items.interactions.pets.InteractionPetFood;
|
||||
import com.eu.habbo.habbohotel.pets.GnomePet;
|
||||
import com.eu.habbo.habbohotel.pets.Pet;
|
||||
import com.eu.habbo.habbohotel.pets.PetTasks;
|
||||
import com.eu.habbo.habbohotel.pets.PetVocalsType;
|
||||
import com.eu.habbo.habbohotel.rooms.RoomUnitStatus;
|
||||
import com.eu.habbo.messages.outgoing.rooms.items.RemoveFloorItemComposer;
|
||||
import com.eu.habbo.messages.outgoing.rooms.users.RoomUserStatusComposer;
|
||||
@@ -22,28 +23,45 @@ public class PetEatAction implements Runnable {
|
||||
@Override
|
||||
public void run() {
|
||||
if (this.pet.getRoomUnit() != null && this.pet.getRoom() != null) {
|
||||
if (this.pet.levelHunger >= 20 && this.food != null && Integer.parseInt(this.food.getExtradata()) < this.food.getBaseItem().getStateCount()) {
|
||||
this.pet.addHunger(-20);
|
||||
// Check if food still has portions left (state < stateCount means food remaining)
|
||||
int currentState = 0;
|
||||
try {
|
||||
currentState = Integer.parseInt(this.food.getExtradata());
|
||||
} catch (NumberFormatException e) {
|
||||
currentState = 0;
|
||||
}
|
||||
|
||||
if (this.pet.levelHunger >= 10 && this.food != null && currentState < this.food.getBaseItem().getStateCount()) {
|
||||
// Say eating vocal on first bite
|
||||
if (currentState == 0 || Emulator.getRandom().nextInt(3) == 0) {
|
||||
this.pet.say(this.pet.getPetData().randomVocal(PetVocalsType.EATING));
|
||||
}
|
||||
|
||||
// Faster eating: reduce 40 hunger per bite instead of 20
|
||||
this.pet.addHunger(-40);
|
||||
this.pet.setTask(PetTasks.EAT);
|
||||
this.pet.getRoomUnit().setCanWalk(false);
|
||||
|
||||
this.food.setExtradata(Integer.valueOf(this.food.getExtradata()) + 1 + "");
|
||||
// Advance food state (each bite uses up a portion)
|
||||
this.food.setExtradata((currentState + 1) + "");
|
||||
this.pet.getRoom().updateItem(this.food);
|
||||
|
||||
if (this.pet instanceof GnomePet) {
|
||||
if (this.pet.getPetData().getType() == 26) {
|
||||
AchievementManager.progressAchievement(Emulator.getGameEnvironment().getHabboManager().getHabbo(this.pet.getUserId()), Emulator.getGameEnvironment().getAchievementManager().getAchievement("GnomeFeeding"), 20);
|
||||
AchievementManager.progressAchievement(Emulator.getGameEnvironment().getHabboManager().getHabbo(this.pet.getUserId()), Emulator.getGameEnvironment().getAchievementManager().getAchievement("GnomeFeeding"), 40);
|
||||
} else {
|
||||
AchievementManager.progressAchievement(Emulator.getGameEnvironment().getHabboManager().getHabbo(this.pet.getUserId()), Emulator.getGameEnvironment().getAchievementManager().getAchievement("LeprechaunFeeding"), 20);
|
||||
AchievementManager.progressAchievement(Emulator.getGameEnvironment().getHabboManager().getHabbo(this.pet.getUserId()), Emulator.getGameEnvironment().getAchievementManager().getAchievement("LeprechaunFeeding"), 40);
|
||||
}
|
||||
} else {
|
||||
AchievementManager.progressAchievement(Emulator.getGameEnvironment().getHabboManager().getHabbo(this.pet.getUserId()), Emulator.getGameEnvironment().getAchievementManager().getAchievement("PetFeeding"), 20);
|
||||
AchievementManager.progressAchievement(Emulator.getGameEnvironment().getHabboManager().getHabbo(this.pet.getUserId()), Emulator.getGameEnvironment().getAchievementManager().getAchievement("PetFeeding"), 40);
|
||||
}
|
||||
|
||||
Emulator.getThreading().run(this, 1000);
|
||||
// Faster eating: 500ms between bites instead of 1000ms
|
||||
Emulator.getThreading().run(this, 500);
|
||||
} else {
|
||||
if (this.food != null && Integer.parseInt(this.food.getExtradata()) == this.food.getBaseItem().getStateCount()) {
|
||||
Emulator.getThreading().run(new QueryDeleteHabboItem(this.food.getId()), 500);
|
||||
// Food is empty - remove it
|
||||
if (this.food != null && currentState >= this.food.getBaseItem().getStateCount()) {
|
||||
Emulator.getThreading().run(new QueryDeleteHabboItem(this.food.getId()), 250);
|
||||
if (this.pet.getRoom() != null) {
|
||||
this.pet.getRoom().removeHabboItem(this.food);
|
||||
this.pet.getRoom().sendComposer(new RemoveFloorItemComposer(this.food, true).compose());
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.eu.habbo.threading.runnables;
|
||||
import com.eu.habbo.Emulator;
|
||||
import com.eu.habbo.habbohotel.pets.Pet;
|
||||
import com.eu.habbo.habbohotel.pets.PetTasks;
|
||||
import com.eu.habbo.habbohotel.pets.PetVocalsType;
|
||||
import com.eu.habbo.habbohotel.rooms.RoomTile;
|
||||
import com.eu.habbo.habbohotel.users.Habbo;
|
||||
|
||||
@@ -19,31 +20,81 @@ public class PetFollowHabbo implements Runnable {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (this.pet != null) {
|
||||
if (this.pet.getTask() != PetTasks.FOLLOW)
|
||||
return;
|
||||
|
||||
if (this.habbo != null) {
|
||||
if (this.habbo.getRoomUnit() != null) {
|
||||
if (this.pet.getRoomUnit() != null) {
|
||||
RoomTile target = this.habbo.getHabboInfo().getCurrentRoom().getLayout().getTileInFront(this.habbo.getRoomUnit().getCurrentLocation(), Math.abs((this.habbo.getRoomUnit().getBodyRotation().getValue() + this.directionOffset + 4) % 8));
|
||||
|
||||
if (target != null) {
|
||||
if (target.x < 0 || target.y < 0)
|
||||
target = this.habbo.getHabboInfo().getCurrentRoom().getLayout().getTileInFront(this.habbo.getRoomUnit().getCurrentLocation(), this.habbo.getRoomUnit().getBodyRotation().getValue());
|
||||
|
||||
if (target.x >= 0 && target.y >= 0) {
|
||||
if (this.pet.getRoom().getLayout().tileWalkable(target.x, target.y)) {
|
||||
this.pet.getRoomUnit().setGoalLocation(target);
|
||||
this.pet.getRoomUnit().setCanWalk(true);
|
||||
this.pet.setTask(PetTasks.FOLLOW);
|
||||
}
|
||||
}
|
||||
Emulator.getThreading().run(this, 500);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Comprehensive null checks
|
||||
if (this.pet == null || this.pet.getRoom() == null || this.pet.getRoomUnit() == null) {
|
||||
return; // Stop following - pet or room is gone
|
||||
}
|
||||
|
||||
// Check if task is any follow type
|
||||
PetTasks task = this.pet.getTask();
|
||||
if (task != PetTasks.FOLLOW && task != PetTasks.FOLLOW_LEFT && task != PetTasks.FOLLOW_RIGHT) {
|
||||
return; // Task was changed, stop
|
||||
}
|
||||
|
||||
// Check if habbo is still valid
|
||||
if (this.habbo == null || this.habbo.getRoomUnit() == null) {
|
||||
this.pet.setTask(PetTasks.FREE);
|
||||
return; // Owner gone, stop following
|
||||
}
|
||||
|
||||
// Check if habbo is still in the same room as the pet
|
||||
if (this.habbo.getHabboInfo().getCurrentRoom() != this.pet.getRoom()) {
|
||||
this.pet.setTask(PetTasks.FREE);
|
||||
this.pet.say(this.pet.getPetData().randomVocal(PetVocalsType.GENERIC_SAD));
|
||||
return;
|
||||
}
|
||||
|
||||
// Calculate target position
|
||||
RoomTile habboTile = this.habbo.getRoomUnit().getCurrentLocation();
|
||||
if (habboTile == null) {
|
||||
Emulator.getThreading().run(this, 500);
|
||||
return;
|
||||
}
|
||||
|
||||
int targetRotation = Math.abs((this.habbo.getRoomUnit().getBodyRotation().getValue()
|
||||
+ this.directionOffset + 4) % 8);
|
||||
|
||||
RoomTile target = this.pet.getRoom().getLayout().getTileInFront(habboTile, targetRotation);
|
||||
|
||||
// Validate target tile - try alternative positions if needed
|
||||
if (target == null || target.x < 0 || target.y < 0
|
||||
|| !this.pet.getRoom().getLayout().tileWalkable(target.x, target.y)) {
|
||||
// Try directly behind habbo
|
||||
target = this.pet.getRoom().getLayout().getTileInFront(
|
||||
habboTile,
|
||||
(this.habbo.getRoomUnit().getBodyRotation().getValue() + 4) % 8
|
||||
);
|
||||
}
|
||||
|
||||
// Try other adjacent positions if still invalid
|
||||
if (target == null || target.x < 0 || target.y < 0
|
||||
|| !this.pet.getRoom().getLayout().tileWalkable(target.x, target.y)) {
|
||||
// Try to the left
|
||||
target = this.pet.getRoom().getLayout().getTileInFront(
|
||||
habboTile,
|
||||
(this.habbo.getRoomUnit().getBodyRotation().getValue() + 2) % 8
|
||||
);
|
||||
}
|
||||
|
||||
if (target == null || target.x < 0 || target.y < 0
|
||||
|| !this.pet.getRoom().getLayout().tileWalkable(target.x, target.y)) {
|
||||
// Try to the right
|
||||
target = this.pet.getRoom().getLayout().getTileInFront(
|
||||
habboTile,
|
||||
(this.habbo.getRoomUnit().getBodyRotation().getValue() + 6) % 8
|
||||
);
|
||||
}
|
||||
|
||||
// If we found a valid target, move there
|
||||
if (target != null && target.x >= 0 && target.y >= 0) {
|
||||
if (this.pet.getRoom().getLayout().tileWalkable(target.x, target.y)) {
|
||||
this.pet.getRoomUnit().setGoalLocation(target);
|
||||
this.pet.getRoomUnit().setCanWalk(true);
|
||||
}
|
||||
}
|
||||
|
||||
// Continue following with slight randomization for natural behavior
|
||||
int nextDelay = 400 + Emulator.getRandom().nextInt(200);
|
||||
Emulator.getThreading().run(this, nextDelay);
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -75,7 +75,8 @@ class FreezeHandleSnowballExplosion implements Runnable {
|
||||
habbos.addAll(this.thrownData.room.getHabbosAt(freezeTile.getX(), freezeTile.getY()));
|
||||
|
||||
for (Habbo habbo : habbos) {
|
||||
if (habbo.getHabboInfo().getGamePlayer() != null && habbo.getHabboInfo().getGamePlayer() instanceof FreezeGamePlayer hPlayer) {
|
||||
if (habbo.getHabboInfo().getGamePlayer() != null && habbo.getHabboInfo().getGamePlayer() instanceof FreezeGamePlayer) {
|
||||
FreezeGamePlayer hPlayer = (FreezeGamePlayer) habbo.getHabboInfo().getGamePlayer();
|
||||
if (!hPlayer.canGetFrozen())
|
||||
continue;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user