🆙 Updates

This commit is contained in:
duckietm
2026-01-07 15:54:18 +01:00
parent 16d88b4422
commit ad2c52d3af
28 changed files with 310 additions and 142 deletions
@@ -40,7 +40,7 @@ public class GamePlayer {
this.wiredScore += amount;
}
WiredManager.triggerScoreAchieved(this.habbo.getHabboInfo().getCurrentRoom(), this.habbo.getRoomUnit(), this.score);
WiredManager.triggerScoreAchieved(this.habbo.getHabboInfo().getCurrentRoom(), this.habbo.getRoomUnit(), this.score, amount);
}
}
@@ -48,7 +48,7 @@ public class InteractionDice extends HabboItem {
if (client != null) {
if (RoomLayout.tilesAdjecent(room.getLayout().getTile(this.getX(), this.getY()), client.getHabbo().getRoomUnit().getCurrentLocation())) {
if (!this.getExtradata().equalsIgnoreCase("-1")) {
FurnitureDiceRolledEvent event = Emulator.getPluginManager().fireEvent(new FurnitureDiceRolledEvent(this, client.getHabbo(), -1));
FurnitureDiceRolledEvent event = (FurnitureDiceRolledEvent) Emulator.getPluginManager().fireEvent(new FurnitureDiceRolledEvent(this, client.getHabbo(), -1));
if (event.isCancelled())
return;
@@ -58,9 +58,9 @@ public class InteractionDice extends HabboItem {
Emulator.getThreading().run(this);
if (event.result > 0) {
Emulator.getThreading().run(new RandomDiceNumber(room, this, event.result), 2000);
Emulator.getThreading().run(new RandomDiceNumber(room, this, event.result), 1500);
} else {
Emulator.getThreading().run(new RandomDiceNumber(this, room, this.getBaseItem().getStateCount()), 2000);
Emulator.getThreading().run(new RandomDiceNumber(this, room, this.getBaseItem().getStateCount()), 1500);
}
}
}
@@ -15,12 +15,12 @@ import java.sql.SQLException;
public class InteractionFXBox extends InteractionDefault {
public InteractionFXBox(ResultSet set, Item baseItem) throws SQLException {
super(set, baseItem);
// this.setExtradata("0");
// this.setExtradata("0");
}
public InteractionFXBox(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) {
super(id, userId, item, extradata, limitedStack, limitedSells);
// this.setExtradata("0");
// this.setExtradata("0");
}
@Override
@@ -1,14 +1,18 @@
package com.eu.habbo.habbohotel.items.interactions;
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.ICycleable;
import com.eu.habbo.habbohotel.items.Item;
import com.eu.habbo.habbohotel.pets.HorsePet;
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.Habbo;
import com.eu.habbo.habbohotel.users.HabboItem;
import com.eu.habbo.messages.ServerMessage;
import com.eu.habbo.threading.runnables.HabboItemNewState;
import gnu.trove.set.hash.THashSet;
import java.sql.ResultSet;
@@ -56,9 +60,10 @@ public class InteractionObstacle extends HabboItem implements ICycleable {
@Override
public void onWalk(RoomUnit roomUnit, Room room, Object[] objects) throws Exception {
/*Pet pet = room.getPet(roomUnit);
Pet pet = room.getPet(roomUnit);
if (pet instanceof HorsePet && ((HorsePet) pet).getRider() != null) {
Habbo rider = ((HorsePet) pet).getRider();
if (pet.getTask() != null && pet.getTask().equals(PetTasks.RIDE)) {
if (pet.getRoomUnit().hasStatus(RoomUnitStatus.JUMP)) {
pet.getRoomUnit().removeStatus(RoomUnitStatus.JUMP);
@@ -75,13 +80,13 @@ public class InteractionObstacle extends HabboItem implements ICycleable {
this.setExtradata(state + "");
pet.getRoomUnit().setStatus(RoomUnitStatus.JUMP, "0");
AchievementManager.progressAchievement(habbo, Emulator.getGameEnvironment().getAchievementManager().getAchievement("HorseConsecutiveJumpsCount"));
AchievementManager.progressAchievement(habbo, Emulator.getGameEnvironment().getAchievementManager().getAchievement("HorseJumping"));
AchievementManager.progressAchievement(rider, Emulator.getGameEnvironment().getAchievementManager().getAchievement("HorseConsecutiveJumpsCount"));
AchievementManager.progressAchievement(rider, Emulator.getGameEnvironment().getAchievementManager().getAchievement("HorseJumping"));
}
room.updateItemState(this);
}
}*/
}
}
@Override
@@ -27,7 +27,7 @@ public class InteractionVendingMachine extends HabboItem {
super(id, userId, item, extradata, limitedStack, limitedSells);
this.setExtradata("0");
}
public THashSet<RoomTile> getActivatorTiles(Room room) {
THashSet<RoomTile> tiles = new THashSet<>();
RoomTile tileInFront = getSquareInFront(room.getLayout(), this);
@@ -6,6 +6,7 @@ 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.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.users.Habbo;
@@ -33,7 +34,11 @@ public class InteractionPetBreedingNest extends HabboItem {
@Override
public boolean canWalkOn(RoomUnit roomUnit, Room room, Object[] objects) {
return room.getPet(roomUnit) != null && !this.boxFull();
Pet pet = room.getPet(roomUnit);
if (pet == null) return false;
// Don't let ridden pets enter breeding nest
if (pet instanceof RideablePet && ((RideablePet) pet).getRider() != null) return false;
return !this.boxFull();
}
@Override
@@ -6,6 +6,7 @@ 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;
@@ -32,6 +33,10 @@ public class InteractionPetDrink extends InteractionDefault {
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.getPetData().haveDrinkItem(this)) {
if (pet.levelThirst >= 35) {
pet.setTask(PetTasks.EAT);
@@ -5,6 +5,7 @@ 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;
@@ -34,6 +35,10 @@ public class InteractionPetFood extends InteractionDefault {
Pet pet = room.getPet(roomUnit);
if (pet != null) {
// Don't let ridden pets eat
if (pet instanceof RideablePet && ((RideablePet) pet).getRider() != null)
return;
if (pet.getPetData().haveFoodItem(this)) {
if (pet.levelHunger >= 35) {
pet.setTask(PetTasks.EAT);
@@ -5,6 +5,7 @@ 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;
@@ -33,6 +34,10 @@ public class InteractionPetToy extends InteractionDefault {
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.getEnergy() <= 35) {
return;
}
@@ -6,10 +6,14 @@ import com.eu.habbo.habbohotel.items.Item;
import com.eu.habbo.habbohotel.items.interactions.InteractionWiredEffect;
import com.eu.habbo.habbohotel.items.interactions.InteractionWiredTrigger;
import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings;
import com.eu.habbo.habbohotel.pets.RideablePet;
import com.eu.habbo.habbohotel.rooms.Room;
import com.eu.habbo.habbohotel.rooms.RoomTile;
import com.eu.habbo.habbohotel.rooms.RoomTileState;
import com.eu.habbo.habbohotel.rooms.RoomUnit;
import com.eu.habbo.habbohotel.rooms.RoomUnitStatus;
import com.eu.habbo.habbohotel.rooms.RoomUnitType;
import com.eu.habbo.habbohotel.users.Habbo;
import com.eu.habbo.habbohotel.wired.core.WiredContext;
import com.eu.habbo.habbohotel.users.HabboItem;
import com.eu.habbo.habbohotel.wired.WiredEffectType;
@@ -17,6 +21,7 @@ import com.eu.habbo.habbohotel.wired.core.WiredManager;
import com.eu.habbo.messages.ServerMessage;
import com.eu.habbo.messages.incoming.wired.WiredSaveException;
import com.eu.habbo.messages.outgoing.rooms.users.RoomUserEffectComposer;
import com.eu.habbo.messages.outgoing.rooms.users.RoomUserStatusComposer;
import com.eu.habbo.threading.runnables.RoomUnitTeleport;
import com.eu.habbo.threading.runnables.SendRoomUnitEffectComposer;
import gnu.trove.procedure.TObjectProcedure;
@@ -54,6 +59,27 @@ public class WiredEffectTeleport extends InteractionWiredEffect {
return;
}
// If this is a rider, sync the riding pet to the rider's current position immediately
// Both will teleport together when the delay fires
if (roomUnit.getRoomUnitType() == RoomUnitType.USER) {
Habbo habbo = room.getHabbo(roomUnit);
if (habbo != null && habbo.getHabboInfo() != null && habbo.getHabboInfo().getRiding() != null) {
RideablePet ridingPet = habbo.getHabboInfo().getRiding();
RoomUnit petUnit = ridingPet.getRoomUnit();
if (petUnit != null) {
// Sync pet to rider's current position
RoomTile riderTile = roomUnit.getCurrentLocation();
petUnit.setLocation(riderTile);
petUnit.setZ(roomUnit.getZ() - 1.0);
petUnit.setPreviousLocation(riderTile);
petUnit.setGoalLocation(riderTile);
petUnit.removeStatus(RoomUnitStatus.MOVE);
petUnit.setCanWalk(false);
room.sendComposer(new RoomUserStatusComposer(petUnit).compose());
}
}
}
// makes a temporary effect
roomUnit.getRoom().unIdle(roomUnit.getRoom().getHabbo(roomUnit));
@@ -126,25 +126,12 @@ public class WiredEffectTriggerStacks extends InteractionWiredEffect {
public void execute(WiredContext ctx) {
Room room = ctx.room();
RoomUnit roomUnit = ctx.actor().orElse(null);
Object[] stuff = ctx.legacyStuff();
// Prevent infinite recursion by checking for WiredEffectTriggerStacks in the call chain
// and limiting the recursion depth
int stackDepth = 0;
if (stuff != null) {
for (Object obj : stuff) {
if (obj instanceof WiredEffectTriggerStacks) {
stackDepth++;
// If this specific stack is already in the chain, prevent infinite loop
if (obj == this) {
return;
}
}
}
}
// Get the current call stack depth from the event
int currentDepth = ctx.event().getCallStackDepth();
// Prevent excessive recursion depth
if (stackDepth >= MAX_STACK_DEPTH) {
if (currentDepth >= MAX_STACK_DEPTH) {
return;
}
@@ -169,17 +156,8 @@ public class WiredEffectTriggerStacks extends InteractionWiredEffect {
}
}
// Create new stuff array with this trigger stack added for recursion tracking
Object[] newStuff;
if (stuff != null) {
newStuff = new Object[stuff.length + 1];
System.arraycopy(stuff, 0, newStuff, 0, stuff.length);
newStuff[newStuff.length - 1] = this;
} else {
newStuff = new Object[] { this };
}
WiredManager.executeEffectsAtTiles(usedTiles, roomUnit, room, newStuff);
// Execute effects at tiles with incremented call stack depth
WiredManager.executeEffectsAtTiles(usedTiles, roomUnit, room, currentDepth + 1);
}
@Deprecated
@@ -120,14 +120,16 @@ public class WiredTriggerBotReachedFurni extends InteractionWiredTrigger {
public boolean matches(HabboItem triggerItem, WiredEvent event) {
RoomUnit roomUnit = event.getActor().orElse(null);
Room room = event.getRoom();
Object[] stuff = event.getLegacyStuff();
if (stuff.length >= 1) {
if (stuff[0] instanceof HabboItem) {
return this.items.contains(stuff[0]) && room.getBots(this.botName).stream().anyMatch(bot -> bot.getRoomUnit() == roomUnit);
}
// Get the furniture item the bot walked onto
HabboItem sourceItem = event.getSourceItem().orElse(null);
if (sourceItem == null || roomUnit == null) {
return false;
}
return false;
// Check if this furniture is in our monitored list AND the actor is the correct bot
return this.items.contains(sourceItem) &&
room.getBots(this.botName).stream().anyMatch(bot -> bot.getRoomUnit() == roomUnit);
}
@Deprecated
@@ -6,9 +6,7 @@ import com.eu.habbo.habbohotel.items.interactions.InteractionWiredTrigger;
import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings;
import com.eu.habbo.habbohotel.rooms.Room;
import com.eu.habbo.habbohotel.rooms.RoomUnit;
import com.eu.habbo.habbohotel.users.Habbo;
import com.eu.habbo.habbohotel.users.HabboItem;
import com.eu.habbo.habbohotel.wired.WiredEffectType;
import com.eu.habbo.habbohotel.wired.core.WiredManager;
import com.eu.habbo.habbohotel.wired.WiredTriggerType;
import com.eu.habbo.habbohotel.wired.core.WiredEvent;
@@ -37,25 +35,14 @@ public class WiredTriggerFurniStateToggled extends InteractionWiredTrigger {
@Override
public boolean matches(HabboItem triggerItem, WiredEvent event) {
RoomUnit roomUnit = event.getActor().orElse(null);
Room room = event.getRoom();
Object[] stuff = event.getLegacyStuff();
// Reject if this was triggered by a wired effect (to prevent loops)
if (event.isTriggeredByEffect()) {
return false;
}
if (stuff.length >= 1) {
Habbo habbo = room.getHabbo(roomUnit);
if (habbo != null) {
for (Object object : stuff) {
if (object instanceof WiredEffectType) {
return false;
}
}
HabboItem sourceItem = event.getSourceItem().orElse(null);
if (sourceItem != null) {
return this.items.contains(sourceItem);
}
}
HabboItem sourceItem = event.getSourceItem().orElse(null);
if (sourceItem != null) {
return this.items.contains(sourceItem);
}
return false;
}
@@ -28,15 +28,11 @@ public class WiredTriggerScoreAchieved extends InteractionWiredTrigger {
@Override
public boolean matches(HabboItem triggerItem, WiredEvent event) {
Object[] stuff = event.getLegacyStuff();
if (stuff.length >= 2) {
int points = (Integer) stuff[0];
int amountAdded = (Integer) stuff[1];
int points = event.getScore();
int amountAdded = event.getScoreAdded();
return points - amountAdded < this.score && points >= this.score;
}
return false;
// Check if this score addition crossed the threshold
return points - amountAdded < this.score && points >= this.score;
}
@Deprecated
@@ -556,6 +556,10 @@ public class Room implements Comparable<Room>, ISerialize, Runnable {
this.cycleManager.resetIdleCycles();
if (this.roomCycleTask != null) {
this.roomCycleTask.cancel(false);
}
this.roomCycleTask = Emulator.getThreading().getService()
.scheduleAtFixedRate(this, 500, 500, TimeUnit.MILLISECONDS);
} catch (Exception e) {
@@ -868,16 +872,20 @@ public class Room implements Comparable<Room>, ISerialize, Runnable {
}
if (this.loaded) {
try {
// Set loaded to false FIRST to prevent re-entry and ensure cycle stops
this.loaded = false;
try {
if (this.traxManager != null && !this.traxManager.disposed()) {
this.traxManager.dispose();
}
this.roomCycleTask.cancel(false);
if (this.roomCycleTask != null) {
this.roomCycleTask.cancel(false);
this.roomCycleTask = null;
}
this.scheduledTasks.clear();
this.scheduledComposers.clear();
this.loaded = false;
this.tileCache.clear();
@@ -901,10 +909,10 @@ public class Room implements Comparable<Room>, ISerialize, Runnable {
if (this.roomSpecialTypes != null) {
this.roomSpecialTypes.dispose();
}
// Unregister all wired tickables for this room from the tick service
com.eu.habbo.habbohotel.wired.core.WiredManager.unregisterRoomTickables(this);
// Clear wired engine caches for this room
if (com.eu.habbo.habbohotel.wired.core.WiredManager.getStackIndex() != null) {
com.eu.habbo.habbohotel.wired.core.WiredManager.getStackIndex().invalidateAll(this);
@@ -279,14 +279,23 @@ public class RoomRollerManager {
Habbo rollingHabbo = this.room.getHabbo(unit);
if (rollingHabbo != null && rollingHabbo.getHabboInfo() != null) {
RideablePet riding = rollingHabbo.getHabboInfo().getRiding();
if (riding != null) {
if (riding != null && riding.getRoomUnit() != null) {
RoomUnit ridingUnit = riding.getRoomUnit();
newZ = ridingUnit.getZ() + zOffset;
double petOldZ = ridingUnit.getZ();
double petNewZ = tileInFront.getStackHeight();
// Update pet position immediately before composing messages to prevent desync
rolledUnitIds.add(ridingUnit.getId());
updatedUnit.remove(ridingUnit);
messages.add(new RoomUnitOnRollerComposer(ridingUnit, roller,
ridingUnit.getCurrentLocation(), ridingUnit.getZ(), tileInFront, newZ,
this.room));
// Compose and send pet roller message first
RoomUnitOnRollerComposer petRollerComposer = new RoomUnitOnRollerComposer(
ridingUnit, roller, ridingUnit.getCurrentLocation(), petOldZ,
tileInFront, petNewZ, this.room);
messages.add(petRollerComposer);
// Update newZ for the rider (1 unit above pet)
newZ = petNewZ + 1.0;
isRiding = true;
}
}
@@ -295,9 +304,12 @@ public class RoomRollerManager {
usersRolledThisTile.add(unit.getId());
rolledUnitIds.add(unit.getId());
updatedUnit.remove(unit);
// For riding users, use pet-relative Z values
double riderOldZ = isRiding ? unit.getZ() : unit.getZ();
double riderNewZ = isRiding ? newZ : (unit.getZ() + zOffset);
messages.add(new RoomUnitOnRollerComposer(unit, roller, unit.getCurrentLocation(),
unit.getZ() + (isRiding ? 1 : 0), tileInFront, newZ + (isRiding ? 1 : 0),
this.room));
riderOldZ, tileInFront, riderNewZ, this.room));
if (itemsOnRoller.isEmpty()) {
HabboItem item = this.room.getTopItemAt(tileInFront.x, tileInFront.y);
@@ -23,18 +23,13 @@ import com.eu.habbo.util.pathfinding.Rotation;
import gnu.trove.map.TMap;
import gnu.trove.map.hash.THashMap;
import gnu.trove.set.hash.THashSet;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Deque;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledFuture;
import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class RoomUnit {
@@ -155,9 +150,11 @@ public class RoomUnit {
if (!this.isWalking() && !this.isKicked) {
if (this.status.remove(RoomUnitStatus.MOVE) == null) {
Habbo habboT = room.getHabbo(this);
if (habboT != null) {
habboT.getHabboInfo().getRiding().getRoomUnit().status.remove(RoomUnitStatus.MOVE);
if (habboT != null && habboT.getHabboInfo() != null && habboT.getHabboInfo().getRiding() != null) {
RoomUnit ridingRoomUnit = habboT.getHabboInfo().getRiding().getRoomUnit();
if (ridingRoomUnit != null) {
ridingRoomUnit.status.remove(RoomUnitStatus.MOVE);
}
}
return true;
}
@@ -480,6 +480,10 @@ public class HabboInfo implements Runnable {
roomUnit.setZ(riding.getRoomUnit().getZ());
roomUnit.setPreviousLocationZ(riding.getRoomUnit().getZ());
roomUnit.stopWalking();
if (riding.getRoomUnit() != null) {
riding.getRoomUnit().setCanWalk(true);
room.sendComposer(new RoomUserStatusComposer(riding.getRoomUnit()).compose());
}
room.sendComposer(new RoomUserStatusComposer(roomUnit).compose());
List<RoomTile> availableTiles = isRemoving ? new ArrayList<>() : this.getCurrentRoom().getLayout().getWalkableTilesAround(roomUnit.getCurrentLocation());
@@ -256,14 +256,6 @@ public final class WiredContext {
return legacySettings != null ? legacySettings : new Object[0];
}
/**
* Get the legacy stuff array from the event.
* @return the legacy stuff array
*/
public Object[] legacyStuff() {
return event.getLegacyStuff();
}
// ========== Utility Methods ==========
/**
@@ -128,11 +128,15 @@ public final class WiredEvent {
private final Type type;
private final Room room;
private final RoomUnit actor; // nullable
private final HabboItem sourceItem; // nullable
private final RoomTile tile; // nullable
private final String text; // nullable
private final Object[] legacyStuff; // for adapter compatibility
private final RoomUnit actor; // nullable - the user/bot that caused the event
private final HabboItem sourceItem; // nullable - the furniture involved
private final RoomTile tile; // nullable - the tile where event occurred
private final String text; // nullable - text for say triggers
private final RoomUnit targetUnit; // nullable - target user (e.g., for bot reached habbo)
private final int score; // score value for score achieved events
private final int scoreAdded; // amount added for score achieved events
private final boolean triggeredByEffect; // true if triggered by a wired effect (to prevent loops)
private final int callStackDepth; // recursion depth for trigger stacks effect
private final long createdAtMs;
private WiredEvent(Builder builder) {
@@ -142,7 +146,11 @@ public final class WiredEvent {
this.sourceItem = builder.sourceItem;
this.tile = builder.tile;
this.text = builder.text;
this.legacyStuff = builder.legacyStuff;
this.targetUnit = builder.targetUnit;
this.score = builder.score;
this.scoreAdded = builder.scoreAdded;
this.triggeredByEffect = builder.triggeredByEffect;
this.callStackDepth = builder.callStackDepth;
this.createdAtMs = builder.createdAtMs;
}
@@ -199,12 +207,46 @@ public final class WiredEvent {
}
/**
* Get the legacy stuff array for adapter compatibility.
* This allows legacy triggers/conditions/effects to work with the new system.
* @return the legacy stuff array, or empty array if not set
* Get the target unit for this event.
* Used for events like BOT_REACHED_HABBO where we need to track the target user.
* @return optional containing the target unit
*/
public Object[] getLegacyStuff() {
return legacyStuff != null ? legacyStuff : new Object[0];
public Optional<RoomUnit> getTargetUnit() {
return Optional.ofNullable(targetUnit);
}
/**
* Get the score value for score achieved events.
* @return the current score
*/
public int getScore() {
return score;
}
/**
* Get the amount of score added for score achieved events.
* @return the amount added
*/
public int getScoreAdded() {
return scoreAdded;
}
/**
* Check if this event was triggered by a wired effect.
* Used to prevent infinite loops (e.g., effect toggles furni -> triggers state changed -> triggers effect).
* @return true if triggered by a wired effect
*/
public boolean isTriggeredByEffect() {
return triggeredByEffect;
}
/**
* Get the call stack depth for recursion tracking.
* Used by WiredEffectTriggerStacks to prevent infinite recursion.
* @return the current recursion depth
*/
public int getCallStackDepth() {
return callStackDepth;
}
/**
@@ -256,7 +298,11 @@ public final class WiredEvent {
private HabboItem sourceItem;
private RoomTile tile;
private String text;
private Object[] legacyStuff;
private RoomUnit targetUnit;
private int score;
private int scoreAdded;
private boolean triggeredByEffect;
private int callStackDepth;
private long createdAtMs = System.currentTimeMillis();
private Builder(Type type, Room room) {
@@ -307,12 +353,52 @@ public final class WiredEvent {
}
/**
* Set the legacy stuff array for adapter compatibility.
* @param stuff the legacy object array
* Set the target unit for this event.
* @param targetUnit the target room unit
* @return this builder
*/
public Builder legacyStuff(Object[] stuff) {
this.legacyStuff = stuff;
public Builder targetUnit(RoomUnit targetUnit) {
this.targetUnit = targetUnit;
return this;
}
/**
* Set the score value for score achieved events.
* @param score the current score
* @return this builder
*/
public Builder score(int score) {
this.score = score;
return this;
}
/**
* Set the amount of score added for score achieved events.
* @param scoreAdded the amount added
* @return this builder
*/
public Builder scoreAdded(int scoreAdded) {
this.scoreAdded = scoreAdded;
return this;
}
/**
* Mark this event as triggered by a wired effect.
* @param triggeredByEffect true if triggered by effect
* @return this builder
*/
public Builder triggeredByEffect(boolean triggeredByEffect) {
this.triggeredByEffect = triggeredByEffect;
return this;
}
/**
* Set the call stack depth for recursion tracking.
* @param callStackDepth the recursion depth
* @return this builder
*/
public Builder callStackDepth(int callStackDepth) {
this.callStackDepth = callStackDepth;
return this;
}
@@ -357,13 +357,17 @@ public final class WiredManager {
/**
* Trigger when score is achieved.
* @param room the room
* @param user the user who scored
* @param score the current total score
* @param scoreAdded the amount of score just added
*/
public static boolean triggerScoreAchieved(Room room, RoomUnit user, int score) {
public static boolean triggerScoreAchieved(Room room, RoomUnit user, int score, int scoreAdded) {
if (!isEnabled() || room == null || user == null) {
return false;
}
WiredEvent event = WiredEvents.scoreAchieved(room, user, score);
WiredEvent event = WiredEvents.scoreAchieved(room, user, score, scoreAdded);
return handleEvent(event);
}
@@ -631,7 +635,15 @@ public final class WiredManager {
// ========== Effect Execution ==========
public static boolean executeEffectsAtTiles(THashSet<RoomTile> tiles, final RoomUnit roomUnit, final Room room, final Object[] stuff) {
/**
* Execute all wired effects at the specified tiles.
* @param tiles the tiles to execute effects at
* @param roomUnit the triggering room unit (may be null)
* @param room the room
* @param callStackDepth current recursion depth for trigger stacks
* @return true if any effects were executed
*/
public static boolean executeEffectsAtTiles(THashSet<RoomTile> tiles, final RoomUnit roomUnit, final Room room, final int callStackDepth) {
for (RoomTile tile : tiles) {
if (room != null) {
THashSet<HabboItem> items = room.getItemsAt(tile);
@@ -642,7 +654,7 @@ public final class WiredManager {
InteractionWiredEffect effect = (InteractionWiredEffect) item;
WiredEvent event = WiredEvent.builder(WiredEvent.Type.CUSTOM, room)
.actor(roomUnit)
.legacyStuff(stuff)
.callStackDepth(callStackDepth)
.build();
WiredContext ctx = new WiredContext(event, effect, DefaultWiredServices.getInstance(), new WiredState(100));
effect.execute(ctx);
@@ -199,19 +199,19 @@ public final class WiredEvents {
.build();
}
/**
* Create an event for when a score is achieved.
/**
* Create an event for when a score threshold is achieved.
* @param room the room
* @param user the user who achieved the score
* @param score the score achieved
* @param score the current total score
* @param scoreAdded the amount of score just added
* @return the event
*/
public static WiredEvent scoreAchieved(Room room, RoomUnit user, int score) {
public static WiredEvent scoreAchieved(Room room, RoomUnit user, int score, int scoreAdded) {
return WiredEvent.builder(WiredEvent.Type.SCORE_ACHIEVED, room)
.actor(user)
.legacyStuff(new Object[]{score})
.score(score)
.scoreAdded(scoreAdded)
.build();
}
@@ -257,7 +257,7 @@ public final class WiredEvents {
return WiredEvent.builder(WiredEvent.Type.BOT_REACHED_HABBO, room)
.actor(botUnit)
.tile(targetUser.getCurrentLocation())
.legacyStuff(new Object[]{targetUser})
.targetUnit(targetUser)
.build();
}
@@ -324,15 +324,14 @@ public final class WiredEvents {
* @param triggerType the legacy trigger type
* @param room the room
* @param roomUnit the triggering unit (may be null)
* @param stuff legacy stuff array
* @param stuff legacy stuff array (now only used to extract typed data)
* @return the event
*/
public static WiredEvent fromLegacy(WiredTriggerType triggerType, Room room, RoomUnit roomUnit, Object[] stuff) {
WiredEvent.Type eventType = WiredEvent.Type.fromLegacyType(triggerType);
WiredEvent.Builder builder = WiredEvent.builder(eventType, room)
.actor(roomUnit)
.legacyStuff(stuff);
.actor(roomUnit);
// Try to extract common data from stuff array
if (stuff != null) {
@@ -252,7 +252,6 @@ public final class WiredTickService {
if (tickables.add(tickable)) {
tickable.onRegistered(room, System.currentTimeMillis());
LOGGER.debug("Registered tickable {} in room {}", tickable.getId(), roomId);
}
}
@@ -273,7 +272,6 @@ public final class WiredTickService {
if (tickables != null) {
if (tickables.remove(tickable)) {
tickable.onUnregistered(room);
LOGGER.debug("Unregistered tickable {} from room {}", tickable.getId(), roomId);
}
// Clean up empty sets
@@ -43,7 +43,7 @@ public class PetInformationComposer extends MessageComposer {
}
this.response.appendInt(this.pet.getEnergy());
this.response.appendInt(this.pet.getMaxEnergy()); //Max energy
this.response.appendInt(this.pet.getHappiness()); //this.pet.getHappiness()
this.response.appendInt(this.pet.getHappiness());
this.response.appendInt(100);
this.response.appendInt(this.pet.getRespect());
this.response.appendInt(this.pet.getUserId());
@@ -51,7 +51,19 @@ public class PetInformationComposer extends MessageComposer {
this.response.appendString(this.room.getFurniOwnerName(this.pet.getUserId())); //Owner name
this.response.appendInt(this.pet instanceof MonsterplantPet ? ((MonsterplantPet) this.pet).getRarity() : 0);
this.response.appendBoolean(this.pet instanceof RideablePet && this.requestingHabbo != null && (((RideablePet) this.pet).getRider() == null || this.pet.getUserId() == this.requestingHabbo.getHabboInfo().getId()) && ((RideablePet) this.pet).hasSaddle()); // can ride
// Can ride: pet has saddle, no one riding or user owns pet, and (user owns pet or anyone can ride)
boolean canRide = false;
if (this.pet instanceof RideablePet && this.requestingHabbo != null) {
RideablePet rideablePet = (RideablePet) this.pet;
boolean hasSaddle = rideablePet.hasSaddle();
boolean noRider = rideablePet.getRider() == null;
boolean isOwner = this.pet.getUserId() == this.requestingHabbo.getHabboInfo().getId();
boolean canRidePermission = isOwner || rideablePet.anyoneCanRide();
canRide = hasSaddle && (noRider || isOwner) && canRidePermission;
}
this.response.appendBoolean(canRide);
this.response.appendBoolean(this.pet instanceof RideablePet && ((RideablePet) this.pet).getRider() != null && this.requestingHabbo != null && ((RideablePet) this.pet).getRider().getHabboInfo().getId() == this.requestingHabbo.getHabboInfo().getId()); // is current user riding
this.response.appendInt(0);
this.response.appendInt(this.pet instanceof RideablePet && ((RideablePet) this.pet).anyoneCanRide() ? 1 : 0); // anyone can ride
@@ -45,7 +45,7 @@ public class RoomPetHorseFigureComposer extends MessageComposer {
this.response.appendInt(this.pet.getHairColor());
}
this.response.appendBoolean(this.pet.hasSaddle());
this.response.appendBoolean(false); // this.pet.anyoneCanRide()
this.response.appendBoolean(this.pet.anyoneCanRide());
return this.response;
}
@@ -4,10 +4,12 @@ import com.eu.habbo.Emulator;
import com.eu.habbo.habbohotel.bots.Bot;
import com.eu.habbo.habbohotel.items.interactions.InteractionRoller;
import com.eu.habbo.habbohotel.pets.Pet;
import com.eu.habbo.habbohotel.pets.RideablePet;
import com.eu.habbo.habbohotel.rooms.Room;
import com.eu.habbo.habbohotel.rooms.RoomTile;
import com.eu.habbo.habbohotel.rooms.RoomUnit;
import com.eu.habbo.habbohotel.rooms.RoomUnitType;
import com.eu.habbo.habbohotel.users.Habbo;
import com.eu.habbo.habbohotel.users.HabboItem;
import com.eu.habbo.messages.ServerMessage;
import com.eu.habbo.messages.outgoing.MessageComposer;
@@ -58,7 +60,21 @@ public class RoomUnitOnRollerComposer extends MessageComposer {
// Early validation: Check if the roller movement is still valid before composing the packet
if (this.roller != null && room.getLayout() != null) {
// Check if the destination tile is blocked by another unit that moved there
if (this.newLocation.hasUnits() && !this.newLocation.getUnits().contains(this.roomUnit)) {
// Skip this check for pets with riders or users riding pets (they share the tile)
boolean isRidingPetOrRider = false;
if (this.roomUnit.getRoomUnitType() == RoomUnitType.PET) {
Pet pet = this.room.getPet(this.roomUnit);
if (pet instanceof RideablePet && ((RideablePet) pet).getRider() != null) {
isRidingPetOrRider = true;
}
} else if (this.roomUnit.getRoomUnitType() == RoomUnitType.USER) {
Habbo habbo = this.room.getHabbo(this.roomUnit);
if (habbo != null && habbo.getHabboInfo() != null && habbo.getHabboInfo().getRiding() != null) {
isRidingPetOrRider = true;
}
}
if (!isRidingPetOrRider && this.newLocation.hasUnits() && !this.newLocation.getUnits().contains(this.roomUnit)) {
return null;
}
@@ -106,6 +122,15 @@ public class RoomUnitOnRollerComposer extends MessageComposer {
Pet pet = this.room.getPet(this.roomUnit);
if (pet != null) {
pet.needsUpdate = true;
// If this pet has a rider, sync the rider's position with the pet
if (pet instanceof RideablePet) {
RideablePet rideablePet = (RideablePet) pet;
Habbo rider = rideablePet.getRider();
if (rider != null && rider.getRoomUnit() != null) {
rider.getRoomUnit().setLastRollerTime(System.currentTimeMillis());
}
}
}
}
}
@@ -21,7 +21,10 @@ public class RoomUnitRidePet implements Runnable {
@Override
public void run() {
if (this.habbo.getRoomUnit() == null || this.pet.getRoomUnit() == null || this.pet.getRoom() != this.habbo.getHabboInfo().getCurrentRoom() || this.goalTile == null || this.habbo.getRoomUnit().getGoal() != this.goalTile)
if (this.habbo == null || this.habbo.getRoomUnit() == null || this.pet == null ||
this.pet.getRoomUnit() == null || this.habbo.getHabboInfo() == null ||
this.pet.getRoom() != this.habbo.getHabboInfo().getCurrentRoom() ||
this.goalTile == null || this.habbo.getRoomUnit().getGoal() != this.goalTile)
return;
if (habbo.getRoomUnit().getCurrentLocation().distance(pet.getRoomUnit().getCurrentLocation()) <= 1) {
@@ -34,10 +37,16 @@ public class RoomUnitRidePet implements Runnable {
habbo.getRoomUnit().setPreviousLocationZ(this.pet.getRoomUnit().getZ() + 1);
habbo.getRoomUnit().setRotation(this.pet.getRoomUnit().getBodyRotation());
habbo.getRoomUnit().statusUpdate(true);
// Set up the pet for riding
pet.setRider(habbo);
pet.setTask(PetTasks.RIDE);
pet.getRoomUnit().stopWalking();
pet.getRoomUnit().setGoalLocation(habbo.getRoomUnit().getCurrentLocation());
habbo.getHabboInfo().getCurrentRoom().sendComposer(new RoomUserStatusComposer(habbo.getRoomUnit()).compose());
habbo.getHabboInfo().getCurrentRoom().sendComposer(new RoomUserEffectComposer(habbo.getRoomUnit()).compose());
pet.setTask(PetTasks.RIDE);
habbo.getHabboInfo().getCurrentRoom().sendComposer(new RoomUserStatusComposer(pet.getRoomUnit()).compose());
} else {
pet.getRoomUnit().setWalkTimeOut(3 + Emulator.getIntUnixTimestamp());
pet.getRoomUnit().stopWalking();