You've already forked Arcturus-Morningstar-Extended
mirror of
https://github.com/duckietm/Arcturus-Morningstar-Extended.git
synced 2026-06-20 15:36:17 +00:00
🆙 update to 4.0.3
This commit is contained in:
@@ -0,0 +1,18 @@
|
|||||||
|
SET NAMES utf8mb4;
|
||||||
|
SET FOREIGN_KEY_CHECKS = 0;
|
||||||
|
|
||||||
|
-- =====================================================
|
||||||
|
-- Update 4.0.2-beta to 4.0.3-beta
|
||||||
|
-- =====================================================
|
||||||
|
|
||||||
|
INSERT INTO `emulator_settings` (`key`, `value`) VALUES
|
||||||
|
-- Maximum pending flood-fill tasks in the executor queue
|
||||||
|
-- Prevents memory leaks from rapid tile locking
|
||||||
|
('hotel.banzai.fill.max_queue', '50'),
|
||||||
|
|
||||||
|
-- Minimum interval (ms) between flood-fill calculations per game
|
||||||
|
-- Prevents errors via rapid wired triggering
|
||||||
|
('hotel.banzai.fill.cooldown_ms', '100')
|
||||||
|
ON DUPLICATE KEY UPDATE `value` = VALUES(`value`);
|
||||||
|
|
||||||
|
SET FOREIGN_KEY_CHECKS = 1;
|
||||||
@@ -0,0 +1,216 @@
|
|||||||
|
-- =====================================================
|
||||||
|
-- Pet Breeding Complete Setup
|
||||||
|
-- =====================================================
|
||||||
|
-- This file sets up all breeding-related data:
|
||||||
|
-- 1. pet_breeding - Maps parent pet types to offspring types
|
||||||
|
-- 2. pet_breeding_races - Defines possible breeds/colors for offspring by rarity
|
||||||
|
-- =====================================================
|
||||||
|
|
||||||
|
-- =====================================================
|
||||||
|
-- SECTION 1: Pet Breeding (Parent -> Offspring Mapping)
|
||||||
|
-- =====================================================
|
||||||
|
-- This table maps which pet type produces which baby type
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS `pet_breeding` (
|
||||||
|
`pet_id` int(11) NOT NULL COMMENT 'Parent pet type',
|
||||||
|
`offspring_id` int(11) NOT NULL COMMENT 'Baby pet type',
|
||||||
|
PRIMARY KEY (`pet_id`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||||
|
|
||||||
|
-- Clear existing data
|
||||||
|
TRUNCATE TABLE `pet_breeding`;
|
||||||
|
|
||||||
|
-- Insert breeding mappings
|
||||||
|
INSERT INTO `pet_breeding` (`pet_id`, `offspring_id`) VALUES
|
||||||
|
(0, 29), -- Dog -> Baby Dog
|
||||||
|
(1, 28), -- Cat -> Baby Cat
|
||||||
|
(3, 25), -- Terrier -> Baby Terrier
|
||||||
|
(4, 24), -- Bear -> Baby Bear
|
||||||
|
(5, 30); -- Pig -> Baby Pig
|
||||||
|
|
||||||
|
-- =====================================================
|
||||||
|
-- SECTION 2: Pet Breeding Races (Offspring Breeds by Rarity)
|
||||||
|
-- =====================================================
|
||||||
|
-- rarity_level: 1=Common, 2=Uncommon, 3=Rare, 4=Epic
|
||||||
|
-- breed: The visual breed/color variant of the baby pet
|
||||||
|
--
|
||||||
|
-- Higher rarity = harder to get, more special colors
|
||||||
|
-- Each baby pet type should have breeds at all 4 rarity levels
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS `pet_breeding_races` (
|
||||||
|
`pet_id` int(11) NOT NULL COMMENT 'Baby pet type (offspring)',
|
||||||
|
`rarity_level` int(11) NOT NULL COMMENT '1=Common, 2=Uncommon, 3=Rare, 4=Epic',
|
||||||
|
`breed` int(11) NOT NULL COMMENT 'Visual breed/color variant',
|
||||||
|
PRIMARY KEY (`pet_id`, `rarity_level`, `breed`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||||
|
|
||||||
|
-- Clear existing data
|
||||||
|
TRUNCATE TABLE `pet_breeding_races`;
|
||||||
|
|
||||||
|
-- =====================================================
|
||||||
|
-- Baby Dog (29) - Offspring of Dog (0) - 20 breeds
|
||||||
|
-- =====================================================
|
||||||
|
INSERT INTO `pet_breeding_races` (`pet_type`, `rarity_level`, `breed`) VALUES
|
||||||
|
-- Common breeds (rarity 1) - Most likely to get
|
||||||
|
(29, 1, 0),
|
||||||
|
(29, 1, 1),
|
||||||
|
(29, 1, 2),
|
||||||
|
(29, 1, 3),
|
||||||
|
(29, 1, 4),
|
||||||
|
(29, 1, 5),
|
||||||
|
(29, 1, 6),
|
||||||
|
(29, 1, 7),
|
||||||
|
-- Uncommon breeds (rarity 2)
|
||||||
|
(29, 2, 8),
|
||||||
|
(29, 2, 9),
|
||||||
|
(29, 2, 10),
|
||||||
|
(29, 2, 11),
|
||||||
|
(29, 2, 12),
|
||||||
|
-- Rare breeds (rarity 3)
|
||||||
|
(29, 3, 13),
|
||||||
|
(29, 3, 14),
|
||||||
|
(29, 3, 15),
|
||||||
|
(29, 3, 16),
|
||||||
|
-- Epic breeds (rarity 4) - Hardest to get
|
||||||
|
(29, 4, 17),
|
||||||
|
(29, 4, 18),
|
||||||
|
(29, 4, 19);
|
||||||
|
|
||||||
|
-- =====================================================
|
||||||
|
-- Baby Cat (28) - Offspring of Cat (1) - 20 breeds
|
||||||
|
-- =====================================================
|
||||||
|
INSERT INTO `pet_breeding_races` (`pet_type`, `rarity_level`, `breed`) VALUES
|
||||||
|
-- Common breeds (rarity 1)
|
||||||
|
(28, 1, 0),
|
||||||
|
(28, 1, 1),
|
||||||
|
(28, 1, 2),
|
||||||
|
(28, 1, 3),
|
||||||
|
(28, 1, 4),
|
||||||
|
(28, 1, 5),
|
||||||
|
(28, 1, 6),
|
||||||
|
(28, 1, 7),
|
||||||
|
-- Uncommon breeds (rarity 2)
|
||||||
|
(28, 2, 8),
|
||||||
|
(28, 2, 9),
|
||||||
|
(28, 2, 10),
|
||||||
|
(28, 2, 11),
|
||||||
|
(28, 2, 12),
|
||||||
|
-- Rare breeds (rarity 3)
|
||||||
|
(28, 3, 13),
|
||||||
|
(28, 3, 14),
|
||||||
|
(28, 3, 15),
|
||||||
|
(28, 3, 16),
|
||||||
|
-- Epic breeds (rarity 4)
|
||||||
|
(28, 4, 17),
|
||||||
|
(28, 4, 18),
|
||||||
|
(28, 4, 19);
|
||||||
|
|
||||||
|
-- =====================================================
|
||||||
|
-- Baby Terrier (25) - Offspring of Terrier (3) - 20 breeds
|
||||||
|
-- =====================================================
|
||||||
|
INSERT INTO `pet_breeding_races` (`pet_type`, `rarity_level`, `breed`) VALUES
|
||||||
|
-- Common breeds (rarity 1)
|
||||||
|
(25, 1, 0),
|
||||||
|
(25, 1, 1),
|
||||||
|
(25, 1, 2),
|
||||||
|
(25, 1, 3),
|
||||||
|
(25, 1, 4),
|
||||||
|
(25, 1, 5),
|
||||||
|
(25, 1, 6),
|
||||||
|
(25, 1, 7),
|
||||||
|
-- Uncommon breeds (rarity 2)
|
||||||
|
(25, 2, 8),
|
||||||
|
(25, 2, 9),
|
||||||
|
(25, 2, 10),
|
||||||
|
(25, 2, 11),
|
||||||
|
(25, 2, 12),
|
||||||
|
-- Rare breeds (rarity 3)
|
||||||
|
(25, 3, 13),
|
||||||
|
(25, 3, 14),
|
||||||
|
(25, 3, 15),
|
||||||
|
(25, 3, 16),
|
||||||
|
-- Epic breeds (rarity 4)
|
||||||
|
(25, 4, 17),
|
||||||
|
(25, 4, 18),
|
||||||
|
(25, 4, 19);
|
||||||
|
|
||||||
|
-- =====================================================
|
||||||
|
-- Baby Bear (24) - Offspring of Bear (4) - 20 breeds
|
||||||
|
-- =====================================================
|
||||||
|
INSERT INTO `pet_breeding_races` (`pet_type`, `rarity_level`, `breed`) VALUES
|
||||||
|
-- Common breeds (rarity 1)
|
||||||
|
(24, 1, 0),
|
||||||
|
(24, 1, 1),
|
||||||
|
(24, 1, 2),
|
||||||
|
(24, 1, 3),
|
||||||
|
(24, 1, 4),
|
||||||
|
(24, 1, 5),
|
||||||
|
(24, 1, 6),
|
||||||
|
(24, 1, 7),
|
||||||
|
-- Uncommon breeds (rarity 2)
|
||||||
|
(24, 2, 8),
|
||||||
|
(24, 2, 9),
|
||||||
|
(24, 2, 10),
|
||||||
|
(24, 2, 11),
|
||||||
|
(24, 2, 12),
|
||||||
|
-- Rare breeds (rarity 3)
|
||||||
|
(24, 3, 13),
|
||||||
|
(24, 3, 14),
|
||||||
|
(24, 3, 15),
|
||||||
|
(24, 3, 16),
|
||||||
|
-- Epic breeds (rarity 4)
|
||||||
|
(24, 4, 17),
|
||||||
|
(24, 4, 18),
|
||||||
|
(24, 4, 19);
|
||||||
|
|
||||||
|
-- =====================================================
|
||||||
|
-- Baby Pig (30) - Offspring of Pig (5) - 20 breeds
|
||||||
|
-- =====================================================
|
||||||
|
INSERT INTO `pet_breeding_races` (`pet_type`, `rarity_level`, `breed`) VALUES
|
||||||
|
-- Common breeds (rarity 1)
|
||||||
|
(30, 1, 0),
|
||||||
|
(30, 1, 1),
|
||||||
|
(30, 1, 2),
|
||||||
|
(30, 1, 3),
|
||||||
|
(30, 1, 4),
|
||||||
|
(30, 1, 5),
|
||||||
|
(30, 1, 6),
|
||||||
|
(30, 1, 7),
|
||||||
|
-- Uncommon breeds (rarity 2)
|
||||||
|
(30, 2, 8),
|
||||||
|
(30, 2, 9),
|
||||||
|
(30, 2, 10),
|
||||||
|
(30, 2, 11),
|
||||||
|
(30, 2, 12),
|
||||||
|
-- Rare breeds (rarity 3)
|
||||||
|
(30, 3, 13),
|
||||||
|
(30, 3, 14),
|
||||||
|
(30, 3, 15),
|
||||||
|
(30, 3, 16),
|
||||||
|
-- Epic breeds (rarity 4)
|
||||||
|
(30, 4, 17),
|
||||||
|
(30, 4, 18),
|
||||||
|
(30, 4, 19);
|
||||||
|
|
||||||
|
-- =====================================================
|
||||||
|
-- Also ensure pet_actions has correct offspring_type values
|
||||||
|
-- =====================================================
|
||||||
|
UPDATE `pet_actions` SET `offspring_type` = 29 WHERE `pet_type` = 0; -- Dog -> Baby Dog
|
||||||
|
UPDATE `pet_actions` SET `offspring_type` = 28 WHERE `pet_type` = 1; -- Cat -> Baby Cat
|
||||||
|
UPDATE `pet_actions` SET `offspring_type` = 25 WHERE `pet_type` = 3; -- Terrier -> Baby Terrier
|
||||||
|
UPDATE `pet_actions` SET `offspring_type` = 24 WHERE `pet_type` = 4; -- Bear -> Baby Bear
|
||||||
|
UPDATE `pet_actions` SET `offspring_type` = 30 WHERE `pet_type` = 5; -- Pig -> Baby Pig
|
||||||
|
|
||||||
|
-- Set non-breedable pets to -1
|
||||||
|
UPDATE `pet_actions` SET `offspring_type` = -1 WHERE `pet_type` NOT IN (0, 1, 3, 4, 5);
|
||||||
|
|
||||||
|
-- =====================================================
|
||||||
|
-- Fix any items_base with leading/trailing spaces in interaction_type
|
||||||
|
-- =====================================================
|
||||||
|
UPDATE `items_base` SET `interaction_type` = TRIM(`interaction_type`);
|
||||||
|
|
||||||
|
-- =====================================================
|
||||||
|
-- Ensure breeding nest items have correct interaction_type
|
||||||
|
-- =====================================================
|
||||||
|
UPDATE `items_base` SET `interaction_type` = 'breeding_nest'
|
||||||
|
WHERE `item_name` LIKE 'pet_breeding_%' AND `interaction_type` != 'breeding_nest';
|
||||||
+1
-1
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
<groupId>com.eu.habbo</groupId>
|
<groupId>com.eu.habbo</groupId>
|
||||||
<artifactId>Habbo</artifactId>
|
<artifactId>Habbo</artifactId>
|
||||||
<version>4.0.2</version>
|
<version>4.0.3</version>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ public final class Emulator {
|
|||||||
|
|
||||||
public final static int MAJOR = 4;
|
public final static int MAJOR = 4;
|
||||||
public final static int MINOR = 0;
|
public final static int MINOR = 0;
|
||||||
public final static int BUILD = 1;
|
public final static int BUILD = 3;
|
||||||
public final static String PREVIEW = "";
|
public final static String PREVIEW = "";
|
||||||
|
|
||||||
public static final String version = "Arcturus Morningstar" + " " + MAJOR + "." + MINOR + "." + BUILD + " " + PREVIEW;
|
public static final String version = "Arcturus Morningstar" + " " + MAJOR + "." + MINOR + "." + BUILD + " " + PREVIEW;
|
||||||
|
|||||||
+40
-4
@@ -21,8 +21,7 @@ import org.slf4j.Logger;
|
|||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.*;
|
||||||
import java.util.concurrent.ThreadPoolExecutor;
|
|
||||||
|
|
||||||
public class BattleBanzaiGame extends Game {
|
public class BattleBanzaiGame extends Game {
|
||||||
private static final Logger LOGGER = LoggerFactory.getLogger(BattleBanzaiGame.class);
|
private static final Logger LOGGER = LoggerFactory.getLogger(BattleBanzaiGame.class);
|
||||||
@@ -39,9 +38,29 @@ public class BattleBanzaiGame extends Game {
|
|||||||
|
|
||||||
public static final int POINTS_LOCK_TILE = Emulator.getConfig().getInt("hotel.banzai.points.tile.lock", 1);
|
public static final int POINTS_LOCK_TILE = Emulator.getConfig().getInt("hotel.banzai.points.tile.lock", 1);
|
||||||
|
|
||||||
private static final ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(Emulator.getConfig().getInt("hotel.banzai.fill.threads", 2));
|
/**
|
||||||
|
* Maximum number of pending flood-fill tasks allowed in the queue.
|
||||||
|
* This prevents memory exhaustion from rapid tile locking via wireds.
|
||||||
|
*/
|
||||||
|
private static final int MAX_PENDING_FILL_TASKS = Emulator.getConfig().getInt("hotel.banzai.fill.max_queue", 50);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Minimum interval in milliseconds between flood-fill calculations for the same game.
|
||||||
|
* This prevents abuse via rapid wired triggering.
|
||||||
|
*/
|
||||||
|
private static final int FLOOD_FILL_COOLDOWN_MS = Emulator.getConfig().getInt("hotel.banzai.fill.cooldown_ms", 100);
|
||||||
|
|
||||||
|
private static final ThreadPoolExecutor executor = new ThreadPoolExecutor(
|
||||||
|
Emulator.getConfig().getInt("hotel.banzai.fill.threads", 2),
|
||||||
|
Emulator.getConfig().getInt("hotel.banzai.fill.threads", 2),
|
||||||
|
60L, TimeUnit.SECONDS,
|
||||||
|
new LinkedBlockingQueue<>(MAX_PENDING_FILL_TASKS),
|
||||||
|
new ThreadPoolExecutor.DiscardOldestPolicy() // Drop oldest task when queue is full
|
||||||
|
);
|
||||||
|
|
||||||
private final THashMap<GameTeamColors, THashSet<HabboItem>> lockedTiles;
|
private final THashMap<GameTeamColors, THashSet<HabboItem>> lockedTiles;
|
||||||
private final THashMap<Integer, HabboItem> gameTiles;
|
private final THashMap<Integer, HabboItem> gameTiles;
|
||||||
|
private volatile long lastFloodFillTime = 0;
|
||||||
private int tileCount;
|
private int tileCount;
|
||||||
private int countDown;
|
private int countDown;
|
||||||
private int countDown2;
|
private int countDown2;
|
||||||
@@ -267,13 +286,27 @@ public class BattleBanzaiGame extends Game {
|
|||||||
|
|
||||||
if (doNotCheckFill) return;
|
if (doNotCheckFill) return;
|
||||||
|
|
||||||
|
// Rate limit flood-fill calculations to prevent memory exhaustion from rapid wired triggering
|
||||||
|
long now = System.currentTimeMillis();
|
||||||
|
if (now - this.lastFloodFillTime < FLOOD_FILL_COOLDOWN_MS) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.lastFloodFillTime = now;
|
||||||
|
|
||||||
|
// Check if executor queue is getting too full (additional safety check)
|
||||||
|
if (executor.getQueue().size() >= MAX_PENDING_FILL_TASKS - 5) {
|
||||||
|
LOGGER.warn("Battle Banzai flood-fill queue is nearly full, skipping calculation to prevent memory issues");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
final int x = item.getX();
|
final int x = item.getX();
|
||||||
final int y = item.getY();
|
final int y = item.getY();
|
||||||
|
|
||||||
final List<List<RoomTile>> filledAreas = new ArrayList<>();
|
final List<List<RoomTile>> filledAreas = new ArrayList<>();
|
||||||
final THashSet<HabboItem> lockedTiles = new THashSet<>(this.lockedTiles.get(teamColor));
|
final THashSet<HabboItem> lockedTiles = new THashSet<>(this.lockedTiles.get(teamColor));
|
||||||
|
|
||||||
executor.execute(() -> {
|
try {
|
||||||
|
executor.execute(() -> {
|
||||||
filledAreas.add(this.floodFill(x, y - 1, lockedTiles, new ArrayList<>(), teamColor));
|
filledAreas.add(this.floodFill(x, y - 1, lockedTiles, new ArrayList<>(), teamColor));
|
||||||
filledAreas.add(this.floodFill(x, y + 1, lockedTiles, new ArrayList<>(), teamColor));
|
filledAreas.add(this.floodFill(x, y + 1, lockedTiles, new ArrayList<>(), teamColor));
|
||||||
filledAreas.add(this.floodFill(x - 1, y, lockedTiles, new ArrayList<>(), teamColor));
|
filledAreas.add(this.floodFill(x - 1, y, lockedTiles, new ArrayList<>(), teamColor));
|
||||||
@@ -299,6 +332,9 @@ public class BattleBanzaiGame extends Game {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
} catch (RejectedExecutionException e) {
|
||||||
|
LOGGER.warn("Battle Banzai flood-fill task rejected - queue is full");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+47
-4
@@ -8,13 +8,14 @@ import com.eu.habbo.habbohotel.pets.PetManager;
|
|||||||
import com.eu.habbo.habbohotel.pets.PetTasks;
|
import com.eu.habbo.habbohotel.pets.PetTasks;
|
||||||
import com.eu.habbo.habbohotel.pets.RideablePet;
|
import com.eu.habbo.habbohotel.pets.RideablePet;
|
||||||
import com.eu.habbo.habbohotel.rooms.Room;
|
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.RoomUnit;
|
||||||
|
import com.eu.habbo.habbohotel.rooms.RoomUnitType;
|
||||||
import com.eu.habbo.habbohotel.users.Habbo;
|
import com.eu.habbo.habbohotel.users.Habbo;
|
||||||
import com.eu.habbo.habbohotel.users.HabboItem;
|
import com.eu.habbo.habbohotel.users.HabboItem;
|
||||||
import com.eu.habbo.messages.ServerMessage;
|
import com.eu.habbo.messages.ServerMessage;
|
||||||
import com.eu.habbo.messages.outgoing.rooms.pets.PetPackageNameValidationComposer;
|
import com.eu.habbo.messages.outgoing.rooms.pets.PetPackageNameValidationComposer;
|
||||||
import com.eu.habbo.messages.outgoing.rooms.pets.breeding.PetBreedingCompleted;
|
import com.eu.habbo.messages.outgoing.rooms.pets.breeding.PetBreedingCompleted;
|
||||||
import com.eu.habbo.messages.outgoing.rooms.pets.breeding.PetBreedingResultComposer;
|
|
||||||
import com.eu.habbo.threading.runnables.QueryDeleteHabboItem;
|
import com.eu.habbo.threading.runnables.QueryDeleteHabboItem;
|
||||||
|
|
||||||
import java.sql.ResultSet;
|
import java.sql.ResultSet;
|
||||||
@@ -73,9 +74,11 @@ public class InteractionPetBreedingNest extends HabboItem {
|
|||||||
Habbo ownerPetTwo = room.getHabbo(this.petTwo.getUserId());
|
Habbo ownerPetTwo = room.getHabbo(this.petTwo.getUserId());
|
||||||
|
|
||||||
if (ownerPetOne != null && ownerPetTwo != null && this.petOne.getPetData().getType() == this.petTwo.getPetData().getType() && this.petOne.getPetData().getOffspringType() != -1) {
|
if (ownerPetOne != null && ownerPetTwo != null && this.petOne.getPetData().getType() == this.petTwo.getPetData().getType() && this.petOne.getPetData().getOffspringType() != -1) {
|
||||||
ownerPetTwo.getClient().sendResponse(new PetBreedingResultComposer(this.getId(), this.petOne.getPetData().getOffspringType(), this.petOne, ownerPetOne.getHabboInfo().getUsername(), this.petTwo, ownerPetTwo.getHabboInfo().getUsername()));
|
// Auto-breed with generated name (client doesn't have breeding dialog)
|
||||||
this.setExtradata("1");
|
String babyName = generateBabyName(this.petOne.getName(), this.petTwo.getName());
|
||||||
room.updateItem(this);
|
this.breed(ownerPetTwo, babyName, this.petOne.getId(), this.petTwo.getId());
|
||||||
|
} else {
|
||||||
|
this.freePets();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -114,6 +117,25 @@ public class InteractionPetBreedingNest extends HabboItem {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allow pets to walk onto this tile even if another pet is already on it.
|
||||||
|
* This is required because breeding nests are 1x1 and need 2 pets to breed.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean canOverrideTile(RoomUnit unit, Room room, RoomTile tile) {
|
||||||
|
// Only allow override for pets when the box isn't full yet
|
||||||
|
if (unit.getRoomUnitType() == RoomUnitType.PET && !this.boxFull()) {
|
||||||
|
Pet pet = room.getPet(unit);
|
||||||
|
if (pet != null) {
|
||||||
|
// Make sure it's the right pet type for this nest
|
||||||
|
if (pet.getPetData() != null && pet.getPetData().getOffspringType() != -1) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public void stopBreeding(Habbo habbo) {
|
public void stopBreeding(Habbo habbo) {
|
||||||
this.setExtradata("0");
|
this.setExtradata("0");
|
||||||
habbo.getHabboInfo().getCurrentRoom().updateItem(this);
|
habbo.getHabboInfo().getCurrentRoom().updateItem(this);
|
||||||
@@ -146,6 +168,27 @@ public class InteractionPetBreedingNest extends HabboItem {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String generateBabyName(String nameOne, String nameTwo) {
|
||||||
|
// Take first half of parent 1's name and second half of parent 2's name
|
||||||
|
int mid1 = Math.max(1, nameOne.length() / 2);
|
||||||
|
int mid2 = nameTwo.length() / 2;
|
||||||
|
|
||||||
|
String part1 = nameOne.substring(0, mid1);
|
||||||
|
String part2 = nameTwo.substring(mid2);
|
||||||
|
|
||||||
|
String combined = part1 + part2;
|
||||||
|
|
||||||
|
// Ensure name is between 1 and 15 characters
|
||||||
|
if (combined.length() > 15) {
|
||||||
|
combined = combined.substring(0, 15);
|
||||||
|
}
|
||||||
|
if (combined.isEmpty()) {
|
||||||
|
combined = "Baby";
|
||||||
|
}
|
||||||
|
|
||||||
|
return combined;
|
||||||
|
}
|
||||||
|
|
||||||
public void breed(Habbo habbo, String name, int petOneId, int petTwoId) {
|
public void breed(Habbo habbo, String name, int petOneId, int petTwoId) {
|
||||||
Emulator.getThreading().run(new QueryDeleteHabboItem(this.getId()));
|
Emulator.getThreading().run(new QueryDeleteHabboItem(this.getId()));
|
||||||
|
|
||||||
|
|||||||
@@ -260,6 +260,17 @@ public class MonsterplantPet extends Pet implements IPetLook {
|
|||||||
|
|
||||||
public void setDeathTimestamp(int deathTimestamp) {
|
public void setDeathTimestamp(int deathTimestamp) {
|
||||||
this.deathTimestamp = deathTimestamp;
|
this.deathTimestamp = deathTimestamp;
|
||||||
|
this.needsUpdate = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Revives a dead monster plant, resetting its death timestamp and hasDied flag.
|
||||||
|
* Call this instead of just setDeathTimestamp when reviving with mnstr_revival.
|
||||||
|
*/
|
||||||
|
public void revive() {
|
||||||
|
this.deathTimestamp = Emulator.getIntUnixTimestamp() + MonsterplantPet.timeToLive;
|
||||||
|
this.hasDied = false; // Reset so achievement can trigger again if plant dies again
|
||||||
|
this.needsUpdate = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getGrowthStage() {
|
public int getGrowthStage() {
|
||||||
@@ -284,6 +295,7 @@ public class MonsterplantPet extends Pet implements IPetLook {
|
|||||||
|
|
||||||
public void setCanBreed(boolean canBreed) {
|
public void setCanBreed(boolean canBreed) {
|
||||||
this.canBreed = canBreed;
|
this.canBreed = canBreed;
|
||||||
|
this.needsUpdate = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean breedable() {
|
public boolean breedable() {
|
||||||
@@ -296,15 +308,28 @@ public class MonsterplantPet extends Pet implements IPetLook {
|
|||||||
|
|
||||||
public void setPubliclyBreedable(boolean isPubliclyBreedable) {
|
public void setPubliclyBreedable(boolean isPubliclyBreedable) {
|
||||||
this.publiclyBreedable = isPubliclyBreedable;
|
this.publiclyBreedable = isPubliclyBreedable;
|
||||||
|
this.needsUpdate = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void breed(MonsterplantPet pet) {
|
public void breed(MonsterplantPet pet) {
|
||||||
|
// Validate both plants can breed
|
||||||
|
if (!this.breedable() || !pet.breedable()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (this.canBreed && pet.canBreed) {
|
if (this.canBreed && pet.canBreed) {
|
||||||
this.canBreed = false;
|
this.canBreed = false;
|
||||||
this.publiclyBreedable = false;
|
this.publiclyBreedable = false;
|
||||||
|
this.needsUpdate = true;
|
||||||
|
|
||||||
pet.setCanBreed(false);
|
pet.setCanBreed(false);
|
||||||
pet.setPubliclyBreedable(false);
|
pet.setPubliclyBreedable(false);
|
||||||
|
// pet.needsUpdate is set by setCanBreed and setPubliclyBreedable
|
||||||
|
|
||||||
|
// Persist changes to database
|
||||||
|
Emulator.getThreading().run(this);
|
||||||
|
Emulator.getThreading().run(pet);
|
||||||
|
|
||||||
this.room.sendComposer(new PetStatusUpdateComposer(pet).compose());
|
this.room.sendComposer(new PetStatusUpdateComposer(pet).compose());
|
||||||
this.room.sendComposer(new PetStatusUpdateComposer(this).compose());
|
this.room.sendComposer(new PetStatusUpdateComposer(this).compose());
|
||||||
|
|
||||||
@@ -387,6 +412,8 @@ public class MonsterplantPet extends Pet implements IPetLook {
|
|||||||
this.setDeathTimestamp(Emulator.getIntUnixTimestamp() + MonsterplantPet.timeToLive);
|
this.setDeathTimestamp(Emulator.getIntUnixTimestamp() + MonsterplantPet.timeToLive);
|
||||||
this.addHappiness(10);
|
this.addHappiness(10);
|
||||||
this.addExperience(10);
|
this.addExperience(10);
|
||||||
|
// needsUpdate is set by setDeathTimestamp, persist to database
|
||||||
|
Emulator.getThreading().run(this);
|
||||||
this.room.sendComposer(new PetStatusUpdateComposer(this).compose());
|
this.room.sendComposer(new PetStatusUpdateComposer(this).compose());
|
||||||
this.room.sendComposer(new RoomPetRespectComposer(this, RoomPetRespectComposer.PET_TREATED).compose());
|
this.room.sendComposer(new RoomPetRespectComposer(this, RoomPetRespectComposer.PET_TREATED).compose());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -122,15 +122,29 @@ public class PetManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static int randomBody(int minimumRarity, boolean isRare) {
|
public static int randomBody(int minimumRarity, boolean isRare) {
|
||||||
int randomRarity = isRare ? random(Math.max(minimumRarity - 1, 0), (MonsterplantPet.bodyRarity.size() - minimumRarity) + (minimumRarity - 1), 2.0) : random(Math.max(minimumRarity - 1, 0), MonsterplantPet.bodyRarity.size(), 2.0);
|
int maxIndex = MonsterplantPet.bodyRarity.size();
|
||||||
|
int randomIndex = isRare
|
||||||
|
? random(Math.max(minimumRarity - 1, 0), Math.min(maxIndex, (maxIndex - minimumRarity) + minimumRarity), 2.0)
|
||||||
|
: random(Math.max(minimumRarity - 1, 0), maxIndex, 2.0);
|
||||||
|
|
||||||
return MonsterplantPet.bodyRarity.get(MonsterplantPet.bodyRarity.keySet().toArray()[randomRarity]).getValue();
|
// Clamp to valid range to prevent ArrayIndexOutOfBoundsException
|
||||||
|
randomIndex = Math.max(0, Math.min(randomIndex, maxIndex - 1));
|
||||||
|
|
||||||
|
// Return the body type KEY (1-12), not the rarity value
|
||||||
|
return (Integer) MonsterplantPet.bodyRarity.keySet().toArray()[randomIndex];
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int randomColor(int minimumRarity, boolean isRare) {
|
public static int randomColor(int minimumRarity, boolean isRare) {
|
||||||
int randomRarity = isRare ? random(Math.max(minimumRarity - 1, 0), (MonsterplantPet.colorRarity.size() - minimumRarity) + (minimumRarity - 1), 2.0) : random(Math.max(minimumRarity - 1, 0), MonsterplantPet.colorRarity.size(), 2.0);
|
int maxIndex = MonsterplantPet.colorRarity.size();
|
||||||
|
int randomIndex = isRare
|
||||||
|
? random(Math.max(minimumRarity - 1, 0), Math.min(maxIndex, (maxIndex - minimumRarity) + minimumRarity), 2.0)
|
||||||
|
: random(Math.max(minimumRarity - 1, 0), maxIndex, 2.0);
|
||||||
|
|
||||||
return MonsterplantPet.colorRarity.get(MonsterplantPet.colorRarity.keySet().toArray()[randomRarity]).getValue();
|
// Clamp to valid range to prevent ArrayIndexOutOfBoundsException
|
||||||
|
randomIndex = Math.max(0, Math.min(randomIndex, maxIndex - 1));
|
||||||
|
|
||||||
|
// Return the color hue KEY (0-10), not the rarity value
|
||||||
|
return (Integer) MonsterplantPet.colorRarity.keySet().toArray()[randomIndex];
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int random(int low, int high, double bias) {
|
public static int random(int low, int high, double bias) {
|
||||||
|
|||||||
+35
-5
@@ -2,24 +2,54 @@ package com.eu.habbo.messages.incoming.rooms.pets;
|
|||||||
|
|
||||||
import com.eu.habbo.habbohotel.pets.MonsterplantPet;
|
import com.eu.habbo.habbohotel.pets.MonsterplantPet;
|
||||||
import com.eu.habbo.habbohotel.pets.Pet;
|
import com.eu.habbo.habbohotel.pets.Pet;
|
||||||
|
import com.eu.habbo.habbohotel.rooms.Room;
|
||||||
import com.eu.habbo.messages.incoming.MessageHandler;
|
import com.eu.habbo.messages.incoming.MessageHandler;
|
||||||
|
|
||||||
public class BreedMonsterplantsEvent extends MessageHandler {
|
public class BreedMonsterplantsEvent extends MessageHandler {
|
||||||
@Override
|
@Override
|
||||||
public void handle() throws Exception {
|
public void handle() throws Exception {
|
||||||
int unknownInt = this.packet.readInt(); //Something state. 2 = accept
|
int unknownInt = this.packet.readInt(); //Something state. 0 = initiate breeding
|
||||||
|
|
||||||
if (unknownInt == 0) {
|
if (unknownInt == 0) {
|
||||||
Pet petOne = this.client.getHabbo().getHabboInfo().getCurrentRoom().getPet(this.packet.readInt());
|
Room room = this.client.getHabbo().getHabboInfo().getCurrentRoom();
|
||||||
Pet petTwo = this.client.getHabbo().getHabboInfo().getCurrentRoom().getPet(this.packet.readInt());
|
if (room == null) return;
|
||||||
|
|
||||||
|
Pet petOne = room.getPet(this.packet.readInt());
|
||||||
|
Pet petTwo = room.getPet(this.packet.readInt());
|
||||||
|
|
||||||
if (petOne == null || petTwo == null || petOne == petTwo) {
|
if (petOne == null || petTwo == null || petOne == petTwo) {
|
||||||
//TODO Add error
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (petOne instanceof MonsterplantPet && petTwo instanceof MonsterplantPet) {
|
if (petOne instanceof MonsterplantPet && petTwo instanceof MonsterplantPet) {
|
||||||
((MonsterplantPet) petOne).breed((MonsterplantPet) petTwo);
|
MonsterplantPet plantOne = (MonsterplantPet) petOne;
|
||||||
|
MonsterplantPet plantTwo = (MonsterplantPet) petTwo;
|
||||||
|
|
||||||
|
// Validate both plants are breedable (fully grown, can breed, not dead)
|
||||||
|
if (!plantOne.breedable() || !plantTwo.breedable()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate ownership - at least one plant must belong to the client
|
||||||
|
// and the other must be publicly breedable or owned by client
|
||||||
|
int clientId = this.client.getHabbo().getHabboInfo().getId();
|
||||||
|
boolean ownsOne = plantOne.getUserId() == clientId;
|
||||||
|
boolean ownsTwo = plantTwo.getUserId() == clientId;
|
||||||
|
|
||||||
|
if (!ownsOne && !ownsTwo) {
|
||||||
|
// Client doesn't own either plant
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If client doesn't own one of them, that one must be publicly breedable
|
||||||
|
if (!ownsOne && !plantOne.isPubliclyBreedable()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!ownsTwo && !plantTwo.isPubliclyBreedable()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
plantOne.breed(plantTwo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
@@ -23,6 +23,8 @@ public class CompostMonsterplantEvent extends MessageHandler {
|
|||||||
int petId = this.packet.readInt();
|
int petId = this.packet.readInt();
|
||||||
|
|
||||||
Room room = this.client.getHabbo().getHabboInfo().getCurrentRoom();
|
Room room = this.client.getHabbo().getHabboInfo().getCurrentRoom();
|
||||||
|
if (room == null) return;
|
||||||
|
|
||||||
Pet pet = room.getPet(petId);
|
Pet pet = room.getPet(petId);
|
||||||
|
|
||||||
if (pet != null) {
|
if (pet != null) {
|
||||||
|
|||||||
+32
-13
@@ -89,12 +89,23 @@ public class PetUseItemEvent extends MessageHandler {
|
|||||||
Emulator.getGameEnvironment().getItemManager().deleteItem(item);
|
Emulator.getGameEnvironment().getItemManager().deleteItem(item);
|
||||||
}
|
}
|
||||||
} else if (pet instanceof MonsterplantPet) {
|
} else if (pet instanceof MonsterplantPet) {
|
||||||
|
// Validate ownership - only owner can use items on their plant
|
||||||
|
if (pet.getUserId() != this.client.getHabbo().getHabboInfo().getId()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
MonsterplantPet monsterplant = (MonsterplantPet) pet;
|
||||||
|
|
||||||
if (item.getBaseItem().getName().equalsIgnoreCase("mnstr_revival")) {
|
if (item.getBaseItem().getName().equalsIgnoreCase("mnstr_revival")) {
|
||||||
if (((MonsterplantPet) pet).isDead()) {
|
if (monsterplant.isDead()) {
|
||||||
((MonsterplantPet) pet).setDeathTimestamp(Emulator.getIntUnixTimestamp() + MonsterplantPet.timeToLive);
|
// Use revive() method which properly resets hasDied flag and sets needsUpdate
|
||||||
|
monsterplant.revive();
|
||||||
pet.getRoomUnit().clearStatus();
|
pet.getRoomUnit().clearStatus();
|
||||||
pet.getRoomUnit().setStatus(RoomUnitStatus.GESTURE, "rev");
|
pet.getRoomUnit().setStatus(RoomUnitStatus.GESTURE, "rev");
|
||||||
((MonsterplantPet) pet).packetUpdate = true;
|
monsterplant.packetUpdate = true;
|
||||||
|
|
||||||
|
// Persist to database
|
||||||
|
Emulator.getThreading().run(pet);
|
||||||
|
|
||||||
this.client.getHabbo().getHabboInfo().getCurrentRoom().removeHabboItem(item);
|
this.client.getHabbo().getHabboInfo().getCurrentRoom().removeHabboItem(item);
|
||||||
this.client.getHabbo().getHabboInfo().getCurrentRoom().sendComposer(new RemoveFloorItemComposer(item).compose());
|
this.client.getHabbo().getHabboInfo().getCurrentRoom().sendComposer(new RemoveFloorItemComposer(item).compose());
|
||||||
@@ -106,12 +117,17 @@ public class PetUseItemEvent extends MessageHandler {
|
|||||||
Emulator.getThreading().run(new QueryDeleteHabboItem(item.getId()));
|
Emulator.getThreading().run(new QueryDeleteHabboItem(item.getId()));
|
||||||
}
|
}
|
||||||
} else if (item.getBaseItem().getName().equalsIgnoreCase("mnstr_fert")) {
|
} else if (item.getBaseItem().getName().equalsIgnoreCase("mnstr_fert")) {
|
||||||
if (!((MonsterplantPet) pet).isFullyGrown()) {
|
if (!monsterplant.isFullyGrown()) {
|
||||||
pet.setCreated(pet.getCreated() - MonsterplantPet.growTime);
|
pet.setCreated(pet.getCreated() - MonsterplantPet.growTime);
|
||||||
|
pet.needsUpdate = true;
|
||||||
pet.getRoomUnit().clearStatus();
|
pet.getRoomUnit().clearStatus();
|
||||||
pet.cycle();
|
pet.cycle();
|
||||||
pet.getRoomUnit().setStatus(RoomUnitStatus.GESTURE, "spd");
|
pet.getRoomUnit().setStatus(RoomUnitStatus.GESTURE, "spd");
|
||||||
pet.getRoomUnit().setStatus(RoomUnitStatus.fromString("grw" + ((MonsterplantPet) pet).getGrowthStage()), "");
|
pet.getRoomUnit().setStatus(RoomUnitStatus.fromString("grw" + monsterplant.getGrowthStage()), "");
|
||||||
|
|
||||||
|
// Persist to database
|
||||||
|
Emulator.getThreading().run(pet);
|
||||||
|
|
||||||
this.client.getHabbo().getHabboInfo().getCurrentRoom().removeHabboItem(item);
|
this.client.getHabbo().getHabboInfo().getCurrentRoom().removeHabboItem(item);
|
||||||
this.client.getHabbo().getHabboInfo().getCurrentRoom().sendComposer(new RemoveFloorItemComposer(item).compose());
|
this.client.getHabbo().getHabboInfo().getCurrentRoom().sendComposer(new RemoveFloorItemComposer(item).compose());
|
||||||
this.client.getHabbo().getHabboInfo().getCurrentRoom().sendComposer(new RoomUserStatusComposer(pet.getRoomUnit()).compose());
|
this.client.getHabbo().getHabboInfo().getCurrentRoom().sendComposer(new RoomUserStatusComposer(pet.getRoomUnit()).compose());
|
||||||
@@ -122,18 +138,21 @@ public class PetUseItemEvent extends MessageHandler {
|
|||||||
Emulator.getThreading().run(new QueryDeleteHabboItem(item.getId()));
|
Emulator.getThreading().run(new QueryDeleteHabboItem(item.getId()));
|
||||||
}
|
}
|
||||||
} else if (item.getBaseItem().getName().startsWith("mnstr_rebreed")) {
|
} else if (item.getBaseItem().getName().startsWith("mnstr_rebreed")) {
|
||||||
if (((MonsterplantPet) pet).isFullyGrown() && !((MonsterplantPet) pet).canBreed()) {
|
if (monsterplant.isFullyGrown() && !monsterplant.canBreed() && !monsterplant.isDead()) {
|
||||||
if (
|
boolean validItem =
|
||||||
(item.getBaseItem().getName().equalsIgnoreCase("mnstr_rebreed") && ((MonsterplantPet) pet).getRarity() <= 5) ||
|
(item.getBaseItem().getName().equalsIgnoreCase("mnstr_rebreed") && monsterplant.getRarity() <= 5) ||
|
||||||
(item.getBaseItem().getName().equalsIgnoreCase("mnstr_rebreed_2") && ((MonsterplantPet) pet).getRarity() >= 6 && ((MonsterplantPet) pet).getRarity() <= 8) ||
|
(item.getBaseItem().getName().equalsIgnoreCase("mnstr_rebreed_2") && monsterplant.getRarity() >= 6 && monsterplant.getRarity() <= 8) ||
|
||||||
(item.getBaseItem().getName().equalsIgnoreCase("mnstr_rebreed_3") && ((MonsterplantPet) pet).getRarity() >= 9)
|
(item.getBaseItem().getName().equalsIgnoreCase("mnstr_rebreed_3") && monsterplant.getRarity() >= 9);
|
||||||
)
|
|
||||||
|
|
||||||
{
|
if (validItem) {
|
||||||
((MonsterplantPet) pet).setCanBreed(true);
|
// setCanBreed now automatically sets needsUpdate
|
||||||
|
monsterplant.setCanBreed(true);
|
||||||
pet.getRoomUnit().clearStatus();
|
pet.getRoomUnit().clearStatus();
|
||||||
pet.getRoomUnit().setStatus(RoomUnitStatus.GESTURE, "reb");
|
pet.getRoomUnit().setStatus(RoomUnitStatus.GESTURE, "reb");
|
||||||
|
|
||||||
|
// Persist to database
|
||||||
|
Emulator.getThreading().run(pet);
|
||||||
|
|
||||||
this.client.getHabbo().getHabboInfo().getCurrentRoom().removeHabboItem(item);
|
this.client.getHabbo().getHabboInfo().getCurrentRoom().removeHabboItem(item);
|
||||||
this.client.getHabbo().getHabboInfo().getCurrentRoom().sendComposer(new RemoveFloorItemComposer(item).compose());
|
this.client.getHabbo().getHabboInfo().getCurrentRoom().sendComposer(new RemoveFloorItemComposer(item).compose());
|
||||||
this.client.getHabbo().getHabboInfo().getCurrentRoom().sendComposer(new RoomUserStatusComposer(pet.getRoomUnit()).compose());
|
this.client.getHabbo().getHabboInfo().getCurrentRoom().sendComposer(new RoomUserStatusComposer(pet.getRoomUnit()).compose());
|
||||||
|
|||||||
+21
-2
@@ -1,20 +1,39 @@
|
|||||||
package com.eu.habbo.messages.incoming.rooms.pets;
|
package com.eu.habbo.messages.incoming.rooms.pets;
|
||||||
|
|
||||||
|
import com.eu.habbo.Emulator;
|
||||||
import com.eu.habbo.habbohotel.pets.MonsterplantPet;
|
import com.eu.habbo.habbohotel.pets.MonsterplantPet;
|
||||||
import com.eu.habbo.habbohotel.pets.Pet;
|
import com.eu.habbo.habbohotel.pets.Pet;
|
||||||
|
import com.eu.habbo.habbohotel.rooms.Room;
|
||||||
import com.eu.habbo.messages.incoming.MessageHandler;
|
import com.eu.habbo.messages.incoming.MessageHandler;
|
||||||
|
import com.eu.habbo.messages.outgoing.rooms.pets.PetStatusUpdateComposer;
|
||||||
|
|
||||||
public class ToggleMonsterplantBreedableEvent extends MessageHandler {
|
public class ToggleMonsterplantBreedableEvent extends MessageHandler {
|
||||||
@Override
|
@Override
|
||||||
public void handle() throws Exception {
|
public void handle() throws Exception {
|
||||||
int petId = this.packet.readInt();
|
int petId = this.packet.readInt();
|
||||||
|
|
||||||
Pet pet = this.client.getHabbo().getHabboInfo().getCurrentRoom().getPet(petId);
|
Room room = this.client.getHabbo().getHabboInfo().getCurrentRoom();
|
||||||
|
if (room == null) return;
|
||||||
|
|
||||||
|
Pet pet = room.getPet(petId);
|
||||||
|
|
||||||
if (pet != null) {
|
if (pet != null) {
|
||||||
if (pet.getUserId() == this.client.getHabbo().getHabboInfo().getId()) {
|
if (pet.getUserId() == this.client.getHabbo().getHabboInfo().getId()) {
|
||||||
if (pet instanceof MonsterplantPet) {
|
if (pet instanceof MonsterplantPet) {
|
||||||
((MonsterplantPet) pet).setPubliclyBreedable(((MonsterplantPet) pet).isPubliclyBreedable());
|
MonsterplantPet monsterplant = (MonsterplantPet) pet;
|
||||||
|
|
||||||
|
// Only allow toggling if plant is breedable (fully grown, can breed, not dead)
|
||||||
|
if (monsterplant.breedable()) {
|
||||||
|
// Toggle the publicly breedable state (was previously setting to same value - bug fix)
|
||||||
|
monsterplant.setPubliclyBreedable(!monsterplant.isPubliclyBreedable());
|
||||||
|
|
||||||
|
// Mark for database update
|
||||||
|
monsterplant.needsUpdate = true;
|
||||||
|
Emulator.getThreading().run(monsterplant);
|
||||||
|
|
||||||
|
// Send status update to room
|
||||||
|
room.sendComposer(new PetStatusUpdateComposer(monsterplant).compose());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-1
@@ -56,6 +56,9 @@ class FreezeHandleSnowballExplosion implements Runnable {
|
|||||||
for (RoomTile roomTile : tiles) {
|
for (RoomTile roomTile : tiles) {
|
||||||
THashSet<HabboItem> items = this.thrownData.room.getItemsAt(roomTile);
|
THashSet<HabboItem> items = this.thrownData.room.getItemsAt(roomTile);
|
||||||
|
|
||||||
|
// Track if we already processed a block at this tile to prevent stacking exploit
|
||||||
|
boolean blockProcessedAtTile = false;
|
||||||
|
|
||||||
for (HabboItem freezeTile : items) {
|
for (HabboItem freezeTile : items) {
|
||||||
if (freezeTile instanceof InteractionFreezeTile || freezeTile instanceof InteractionFreezeBlock) {
|
if (freezeTile instanceof InteractionFreezeTile || freezeTile instanceof InteractionFreezeBlock) {
|
||||||
int distance = 0;
|
int distance = 0;
|
||||||
@@ -93,9 +96,12 @@ class FreezeHandleSnowballExplosion implements Runnable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (freezeTile instanceof InteractionFreezeBlock) {
|
} else if (freezeTile instanceof InteractionFreezeBlock) {
|
||||||
if (freezeTile.getExtradata().equalsIgnoreCase("0")) {
|
// Only process ONE block per tile to prevent stacking exploit
|
||||||
|
// Stacking many blocks and exploding them causes massive lag
|
||||||
|
if (!blockProcessedAtTile && freezeTile.getExtradata().equalsIgnoreCase("0")) {
|
||||||
game.explodeBox((InteractionFreezeBlock) freezeTile, distance * 100);
|
game.explodeBox((InteractionFreezeBlock) freezeTile, distance * 100);
|
||||||
player.addScore(FreezeGame.DESTROY_BLOCK_POINTS);
|
player.addScore(FreezeGame.DESTROY_BLOCK_POINTS);
|
||||||
|
blockProcessedAtTile = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user