You've already forked Arcturus-Morningstar-Extended
mirror of
https://github.com/duckietm/Arcturus-Morningstar-Extended.git
synced 2026-06-19 15:06:19 +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>
|
||||
<artifactId>Habbo</artifactId>
|
||||
<version>4.0.2</version>
|
||||
<version>4.0.3</version>
|
||||
|
||||
<properties>
|
||||
<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 MINOR = 0;
|
||||
public final static int BUILD = 1;
|
||||
public final static int BUILD = 3;
|
||||
public final static String 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 java.util.*;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.*;
|
||||
|
||||
public class BattleBanzaiGame extends Game {
|
||||
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);
|
||||
|
||||
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<Integer, HabboItem> gameTiles;
|
||||
private volatile long lastFloodFillTime = 0;
|
||||
private int tileCount;
|
||||
private int countDown;
|
||||
private int countDown2;
|
||||
@@ -267,13 +286,27 @@ public class BattleBanzaiGame extends Game {
|
||||
|
||||
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 y = item.getY();
|
||||
|
||||
final List<List<RoomTile>> filledAreas = new ArrayList<>();
|
||||
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 - 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.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.rooms.pets.PetPackageNameValidationComposer;
|
||||
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 java.sql.ResultSet;
|
||||
@@ -73,9 +74,11 @@ public class InteractionPetBreedingNest extends HabboItem {
|
||||
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) {
|
||||
ownerPetTwo.getClient().sendResponse(new PetBreedingResultComposer(this.getId(), this.petOne.getPetData().getOffspringType(), this.petOne, ownerPetOne.getHabboInfo().getUsername(), this.petTwo, ownerPetTwo.getHabboInfo().getUsername()));
|
||||
this.setExtradata("1");
|
||||
room.updateItem(this);
|
||||
// Auto-breed with generated name (client doesn't have breeding dialog)
|
||||
String babyName = generateBabyName(this.petOne.getName(), this.petTwo.getName());
|
||||
this.breed(ownerPetTwo, babyName, this.petOne.getId(), this.petTwo.getId());
|
||||
} else {
|
||||
this.freePets();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -114,6 +117,25 @@ public class InteractionPetBreedingNest extends HabboItem {
|
||||
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) {
|
||||
this.setExtradata("0");
|
||||
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) {
|
||||
Emulator.getThreading().run(new QueryDeleteHabboItem(this.getId()));
|
||||
|
||||
|
||||
@@ -260,6 +260,17 @@ public class MonsterplantPet extends Pet implements IPetLook {
|
||||
|
||||
public void setDeathTimestamp(int 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() {
|
||||
@@ -284,6 +295,7 @@ public class MonsterplantPet extends Pet implements IPetLook {
|
||||
|
||||
public void setCanBreed(boolean canBreed) {
|
||||
this.canBreed = canBreed;
|
||||
this.needsUpdate = true;
|
||||
}
|
||||
|
||||
public boolean breedable() {
|
||||
@@ -296,15 +308,28 @@ public class MonsterplantPet extends Pet implements IPetLook {
|
||||
|
||||
public void setPubliclyBreedable(boolean isPubliclyBreedable) {
|
||||
this.publiclyBreedable = isPubliclyBreedable;
|
||||
this.needsUpdate = true;
|
||||
}
|
||||
|
||||
public void breed(MonsterplantPet pet) {
|
||||
// Validate both plants can breed
|
||||
if (!this.breedable() || !pet.breedable()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.canBreed && pet.canBreed) {
|
||||
this.canBreed = false;
|
||||
this.publiclyBreedable = false;
|
||||
this.needsUpdate = true;
|
||||
|
||||
pet.setCanBreed(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(this).compose());
|
||||
|
||||
@@ -387,6 +412,8 @@ public class MonsterplantPet extends Pet implements IPetLook {
|
||||
this.setDeathTimestamp(Emulator.getIntUnixTimestamp() + MonsterplantPet.timeToLive);
|
||||
this.addHappiness(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 RoomPetRespectComposer(this, RoomPetRespectComposer.PET_TREATED).compose());
|
||||
}
|
||||
|
||||
@@ -122,15 +122,29 @@ public class PetManager {
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
// Clamp to valid range to prevent ArrayIndexOutOfBoundsException
|
||||
randomIndex = Math.max(0, Math.min(randomIndex, maxIndex - 1));
|
||||
|
||||
return MonsterplantPet.bodyRarity.get(MonsterplantPet.bodyRarity.keySet().toArray()[randomRarity]).getValue();
|
||||
// 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) {
|
||||
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);
|
||||
|
||||
// Clamp to valid range to prevent ArrayIndexOutOfBoundsException
|
||||
randomIndex = Math.max(0, Math.min(randomIndex, maxIndex - 1));
|
||||
|
||||
return MonsterplantPet.colorRarity.get(MonsterplantPet.colorRarity.keySet().toArray()[randomRarity]).getValue();
|
||||
// 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) {
|
||||
|
||||
+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.Pet;
|
||||
import com.eu.habbo.habbohotel.rooms.Room;
|
||||
import com.eu.habbo.messages.incoming.MessageHandler;
|
||||
|
||||
public class BreedMonsterplantsEvent extends MessageHandler {
|
||||
@Override
|
||||
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) {
|
||||
Pet petOne = this.client.getHabbo().getHabboInfo().getCurrentRoom().getPet(this.packet.readInt());
|
||||
Pet petTwo = this.client.getHabbo().getHabboInfo().getCurrentRoom().getPet(this.packet.readInt());
|
||||
Room room = this.client.getHabbo().getHabboInfo().getCurrentRoom();
|
||||
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) {
|
||||
//TODO Add error
|
||||
return;
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
Room room = this.client.getHabbo().getHabboInfo().getCurrentRoom();
|
||||
if (room == null) return;
|
||||
|
||||
Pet pet = room.getPet(petId);
|
||||
|
||||
if (pet != null) {
|
||||
|
||||
+32
-13
@@ -89,12 +89,23 @@ public class PetUseItemEvent extends MessageHandler {
|
||||
Emulator.getGameEnvironment().getItemManager().deleteItem(item);
|
||||
}
|
||||
} 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 (((MonsterplantPet) pet).isDead()) {
|
||||
((MonsterplantPet) pet).setDeathTimestamp(Emulator.getIntUnixTimestamp() + MonsterplantPet.timeToLive);
|
||||
if (monsterplant.isDead()) {
|
||||
// Use revive() method which properly resets hasDied flag and sets needsUpdate
|
||||
monsterplant.revive();
|
||||
pet.getRoomUnit().clearStatus();
|
||||
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().sendComposer(new RemoveFloorItemComposer(item).compose());
|
||||
@@ -106,12 +117,17 @@ public class PetUseItemEvent extends MessageHandler {
|
||||
Emulator.getThreading().run(new QueryDeleteHabboItem(item.getId()));
|
||||
}
|
||||
} else if (item.getBaseItem().getName().equalsIgnoreCase("mnstr_fert")) {
|
||||
if (!((MonsterplantPet) pet).isFullyGrown()) {
|
||||
if (!monsterplant.isFullyGrown()) {
|
||||
pet.setCreated(pet.getCreated() - MonsterplantPet.growTime);
|
||||
pet.needsUpdate = true;
|
||||
pet.getRoomUnit().clearStatus();
|
||||
pet.cycle();
|
||||
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().sendComposer(new RemoveFloorItemComposer(item).compose());
|
||||
this.client.getHabbo().getHabboInfo().getCurrentRoom().sendComposer(new RoomUserStatusComposer(pet.getRoomUnit()).compose());
|
||||
@@ -122,17 +138,20 @@ public class PetUseItemEvent extends MessageHandler {
|
||||
Emulator.getThreading().run(new QueryDeleteHabboItem(item.getId()));
|
||||
}
|
||||
} else if (item.getBaseItem().getName().startsWith("mnstr_rebreed")) {
|
||||
if (((MonsterplantPet) pet).isFullyGrown() && !((MonsterplantPet) pet).canBreed()) {
|
||||
if (
|
||||
(item.getBaseItem().getName().equalsIgnoreCase("mnstr_rebreed") && ((MonsterplantPet) pet).getRarity() <= 5) ||
|
||||
(item.getBaseItem().getName().equalsIgnoreCase("mnstr_rebreed_2") && ((MonsterplantPet) pet).getRarity() >= 6 && ((MonsterplantPet) pet).getRarity() <= 8) ||
|
||||
(item.getBaseItem().getName().equalsIgnoreCase("mnstr_rebreed_3") && ((MonsterplantPet) pet).getRarity() >= 9)
|
||||
)
|
||||
if (monsterplant.isFullyGrown() && !monsterplant.canBreed() && !monsterplant.isDead()) {
|
||||
boolean validItem =
|
||||
(item.getBaseItem().getName().equalsIgnoreCase("mnstr_rebreed") && monsterplant.getRarity() <= 5) ||
|
||||
(item.getBaseItem().getName().equalsIgnoreCase("mnstr_rebreed_2") && monsterplant.getRarity() >= 6 && monsterplant.getRarity() <= 8) ||
|
||||
(item.getBaseItem().getName().equalsIgnoreCase("mnstr_rebreed_3") && monsterplant.getRarity() >= 9);
|
||||
|
||||
{
|
||||
((MonsterplantPet) pet).setCanBreed(true);
|
||||
if (validItem) {
|
||||
// setCanBreed now automatically sets needsUpdate
|
||||
monsterplant.setCanBreed(true);
|
||||
pet.getRoomUnit().clearStatus();
|
||||
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().sendComposer(new RemoveFloorItemComposer(item).compose());
|
||||
|
||||
+21
-2
@@ -1,20 +1,39 @@
|
||||
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.Pet;
|
||||
import com.eu.habbo.habbohotel.rooms.Room;
|
||||
import com.eu.habbo.messages.incoming.MessageHandler;
|
||||
import com.eu.habbo.messages.outgoing.rooms.pets.PetStatusUpdateComposer;
|
||||
|
||||
public class ToggleMonsterplantBreedableEvent extends MessageHandler {
|
||||
@Override
|
||||
public void handle() throws Exception {
|
||||
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.getUserId() == this.client.getHabbo().getHabboInfo().getId()) {
|
||||
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) {
|
||||
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) {
|
||||
if (freezeTile instanceof InteractionFreezeTile || freezeTile instanceof InteractionFreezeBlock) {
|
||||
int distance = 0;
|
||||
@@ -93,9 +96,12 @@ class FreezeHandleSnowballExplosion implements Runnable {
|
||||
}
|
||||
}
|
||||
} 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);
|
||||
player.addScore(FreezeGame.DESTROY_BLOCK_POINTS);
|
||||
blockProcessedAtTile = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user