🆙 Added Pets play

- Max 5 Pets per room can play
- Max 5 Pets per user in any room can play
This commit is contained in:
duckietm
2026-03-03 14:56:10 +01:00
parent aae2ba7970
commit 8155c4bd14
6 changed files with 126 additions and 9 deletions
@@ -159,4 +159,16 @@ public abstract class InteractionPushable extends InteractionDefault {
public abstract boolean canStillMove(Room room, RoomTile from, RoomTile to, RoomUserRotation direction, RoomUnit kicker, int nextRoll, int currentStep, int totalSteps);
public boolean isMoving() {
return this.currentThread != null && !this.currentThread.dead;
}
protected void initiateKick(Room room, RoomUnit kicker, int velocity, RoomUserRotation direction) {
if (velocity <= 0) return;
if (this.currentThread != null)
this.currentThread.dead = true;
this.currentThread = new KickBallAction(this, room, kicker, direction, velocity, false);
Emulator.getThreading().run(this.currentThread, 0);
}
}
@@ -7,6 +7,8 @@ import com.eu.habbo.habbohotel.items.Item;
import com.eu.habbo.habbohotel.items.interactions.InteractionPushable;
import com.eu.habbo.habbohotel.items.interactions.games.InteractionGameTeamItem;
import com.eu.habbo.habbohotel.items.interactions.games.football.goals.InteractionFootballGoal;
import com.eu.habbo.habbohotel.pets.Pet;
import com.eu.habbo.habbohotel.pets.PetTasks;
import com.eu.habbo.habbohotel.rooms.*;
import com.eu.habbo.habbohotel.users.HabboItem;
import com.eu.habbo.messages.outgoing.rooms.items.ItemStateComposer;
@@ -15,10 +17,13 @@ import com.eu.habbo.util.pathfinding.Rotation;
import java.math.BigDecimal;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Collection;
public class InteractionFootball extends InteractionPushable {
public static final int MAX_FOOTBALL_PETS = 5;
public InteractionFootball(ResultSet set, Item baseItem) throws SQLException {
super(set, baseItem);
}
@@ -194,6 +199,28 @@ public class InteractionFootball extends InteractionPushable {
//Events
private void triggerPetFollowBall(Room room, RoomTile ballTile) {
if (ballTile == null) return;
Collection<Pet> pets = room.getUnitManager().getPets();
if (pets.isEmpty()) return;
for (Pet pet : pets) {
if (pet.getRoomUnit() == null) continue;
if (pet.getTask() == PetTasks.PLAY_FOOTBALL) {
pet.getRoomUnit().setGoalLocation(ballTile);
}
}
}
public void petKick(Room room, RoomUnit petUnit) {
if (isMoving()) return;
int velocity = this.getWalkOnVelocity(petUnit, room);
RoomUserRotation direction = this.getWalkOnDirection(petUnit, room);
this.onKick(room, petUnit, velocity, direction);
this.initiateKick(room, petUnit, velocity, direction);
}
@Override
public void onDrag(Room room, RoomUnit roomUnit, int velocity, RoomUserRotation direction) {
@@ -201,7 +228,7 @@ public class InteractionFootball extends InteractionPushable {
@Override
public void onKick(Room room, RoomUnit roomUnit, int velocity, RoomUserRotation direction) {
triggerPetFollowBall(room, room.getLayout().getTile(this.getX(), this.getY()));
}
@Override
@@ -243,6 +270,8 @@ public class InteractionFootball extends InteractionPushable {
public void onStop(Room room, RoomUnit kicker, int currentStep, int totalSteps) {
this.setExtradata("0");
room.sendComposer(new ItemStateComposer(this).compose());
triggerPetFollowBall(room, room.getLayout().getTile(this.getX(), this.getY()));
}
@Override
@@ -3,6 +3,7 @@ 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.games.football.InteractionFootball;
import com.eu.habbo.habbohotel.items.interactions.pets.InteractionPetToy;
import com.eu.habbo.habbohotel.items.interactions.pets.InteractionPetTree;
import com.eu.habbo.habbohotel.rooms.*;
@@ -25,10 +26,12 @@ import java.sql.*;
import java.util.Calendar;
import java.util.Map;
import java.util.TimeZone;
import java.util.concurrent.atomic.AtomicInteger;
public class Pet implements ISerialize, Runnable {
private static final Logger LOGGER = LoggerFactory.getLogger(Pet.class);
public static final AtomicInteger GLOBAL_FOOTBALL_PET_COUNT = new AtomicInteger(0);
public int levelThirst;
public int levelHunger;
public boolean needsUpdate = false;
@@ -56,7 +59,8 @@ public class Pet implements ISerialize, Runnable {
private int stayStartedAt = 0;
private int idleCommandTicks = 0;
private int freeCommandTicks = -1;
private InteractionFootball cachedBall = null;
// Command cooldown tracking to prevent spam
private int lastCommandId = -1;
private long lastCommandTime = 0;
@@ -321,6 +325,10 @@ public class Pet implements ISerialize, Runnable {
this.task = null;
this.getRoomUnit().setCanWalk(true);
}
if (this.task == PetTasks.PLAY_FOOTBALL) {
this.chaseBall();
}
} else {
int timeout = Emulator.getRandom().nextInt(10) * 2;
this.roomUnit.setWalkTimeOut(timeout < 20 ? 20 + time : timeout + time);
@@ -389,11 +397,66 @@ public class Pet implements ISerialize, Runnable {
this.getRoomUnit().setCanWalk(true);
}
if (this.task == PetTasks.PLAY_FOOTBALL) {
this.setTask(null);
this.getRoomUnit().setCanWalk(true);
}
command.handle(this, habbo, data);
}
private void chaseBall() {
RoomTile petTile = this.roomUnit.getCurrentLocation();
if (petTile == null) {
this.setTask(PetTasks.FREE);
return;
}
if (this.cachedBall != null && this.cachedBall.getRoomId() != this.room.getId()) {
this.cachedBall = null;
this.setTask(PetTasks.FREE);
return;
}
if (this.cachedBall != null && this.cachedBall.isMoving()) {
return;
}
if (this.cachedBall == null) {
InteractionFootball nearest = null;
double nearestDistance = Double.MAX_VALUE;
for (HabboItem item : this.room.getFloorItems()) {
if (item instanceof InteractionFootball ball) {
RoomTile ballTile = this.room.getLayout().getTile(ball.getX(), ball.getY());
if (ballTile != null) {
double dist = petTile.distance(ballTile);
if (dist < nearestDistance) {
nearestDistance = dist;
nearest = ball;
}
}
}
}
if (nearest == null) {
// No ball in room — stop playing
this.setTask(PetTasks.FREE);
return;
}
this.cachedBall = nearest;
}
RoomTile ballTile = this.room.getLayout().getTile(this.cachedBall.getX(), this.cachedBall.getY());
if (RoomLayout.tilesAdjecent(petTile, ballTile)) {
// Adjacent — kick the ball
this.cachedBall.petKick(this.room, this.roomUnit);
} else {
// Still walking toward the ball
this.roomUnit.setGoalLocation(ballTile);
}
}
public boolean canWalk() {
if (this.task == null)
return true;
@@ -506,7 +569,7 @@ public class Pet implements ISerialize, Runnable {
} else {
this.roomUnit.setStatus(RoomUnitStatus.LAY, this.room.getStackHeight(this.roomUnit.getX(), this.roomUnit.getY(), false) + "");
this.say(this.petData.randomVocal(PetVocalsType.SLEEPING));
this.task = PetTasks.DOWN;
this.setTask(PetTasks.DOWN);
}
}
@@ -706,7 +769,7 @@ public class Pet implements ISerialize, Runnable {
public void freeCommand() {
this.task = null;
this.setTask(null);
this.roomUnit.setGoalLocation(this.getRoomUnit().getCurrentLocation());
this.roomUnit.clearStatus();
this.roomUnit.setCanWalk(true);
@@ -840,6 +903,12 @@ public class Pet implements ISerialize, Runnable {
}
public void setTask(PetTasks newTask) {
if (this.task == PetTasks.PLAY_FOOTBALL && newTask != PetTasks.PLAY_FOOTBALL) {
GLOBAL_FOOTBALL_PET_COUNT.decrementAndGet();
this.cachedBall = null; // release cached ball reference
} else if (this.task != PetTasks.PLAY_FOOTBALL && newTask == PetTasks.PLAY_FOOTBALL) {
GLOBAL_FOOTBALL_PET_COUNT.incrementAndGet();
}
this.task = newTask;
}
@@ -80,7 +80,6 @@ public class PetCommand implements Comparable<PetCommand> {
if (Emulator.getRandom().nextInt(100) >= obeyChance) {
pet.say(pet.getPetData().randomVocal(PetVocalsType.DISOBEY));
pet.recordCommandExecution(this.id);
return;
}
@@ -1,6 +1,7 @@
package com.eu.habbo.habbohotel.pets.actions;
import com.eu.habbo.habbohotel.items.interactions.InteractionPushable;
import com.eu.habbo.habbohotel.items.interactions.games.football.InteractionFootball;
import com.eu.habbo.habbohotel.pets.Pet;
import com.eu.habbo.habbohotel.pets.PetAction;
import com.eu.habbo.habbohotel.pets.PetTasks;
@@ -9,6 +10,7 @@ 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;
import com.eu.habbo.messages.outgoing.generic.alerts.GenericAlertComposer;
public class ActionPlayFootball extends PetAction {
public ActionPlayFootball() {
@@ -22,12 +24,20 @@ public class ActionPlayFootball extends PetAction {
if (room == null || room.getLayout() == null) {
return false;
}
if (pet.getRoomUnit() == null) {
return false;
}
// Find the nearest ball to the pet
if (pet.getTask() != PetTasks.PLAY_FOOTBALL
&& Pet.GLOBAL_FOOTBALL_PET_COUNT.get() >= InteractionFootball.MAX_FOOTBALL_PETS) {
if (habbo != null && habbo.getClient() != null) {
habbo.getClient().sendResponse(new GenericAlertComposer(
"Sorry, you already have 5 pets playing football in the rooms"));
}
return false;
}
HabboItem nearestBall = null;
double nearestDistance = Double.MAX_VALUE;
RoomTile petTile = pet.getRoomUnit().getCurrentLocation();
@@ -50,12 +60,10 @@ public class ActionPlayFootball extends PetAction {
}
if (nearestBall == null) {
// No ball in room - disobey
pet.say(pet.getPetData().randomVocal(PetVocalsType.DISOBEY));
return false;
}
// 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()));