diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/InteractionPushable.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/InteractionPushable.java index a8d0db0e..f8e755d5 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/InteractionPushable.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/InteractionPushable.java @@ -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); + } + } \ No newline at end of file diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/games/football/InteractionFootball.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/games/football/InteractionFootball.java index 6bd21167..b40989d2 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/games/football/InteractionFootball.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/games/football/InteractionFootball.java @@ -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 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 diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/pets/Pet.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/pets/Pet.java index c28e39f0..ae70b537 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/pets/Pet.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/pets/Pet.java @@ -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; } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/pets/PetCommand.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/pets/PetCommand.java index d8f2e8e8..b05182f3 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/pets/PetCommand.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/pets/PetCommand.java @@ -80,7 +80,6 @@ public class PetCommand implements Comparable { if (Emulator.getRandom().nextInt(100) >= obeyChance) { pet.say(pet.getPetData().randomVocal(PetVocalsType.DISOBEY)); - pet.recordCommandExecution(this.id); return; } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/pets/actions/ActionPlayFootball.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/pets/actions/ActionPlayFootball.java index a7e825c2..10a3c892 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/pets/actions/ActionPlayFootball.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/pets/actions/ActionPlayFootball.java @@ -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())); diff --git a/Latest_Compiled_Version/Habbo-4.0.5-jar-with-dependencies.jar b/Latest_Compiled_Version/Habbo-4.0.5-jar-with-dependencies.jar index 49cfefa1..55283556 100644 Binary files a/Latest_Compiled_Version/Habbo-4.0.5-jar-with-dependencies.jar and b/Latest_Compiled_Version/Habbo-4.0.5-jar-with-dependencies.jar differ