[Release] Solo PvP Zone System
🔹 Compatible with: aCis 401+
📜 Features:
✅ Automatic Exit on Restart: Players are removed from the zone if a restart occurs or logout.
✅ Custom Exit Command: Players can exit the Solo Zone with the voice command .exit.
✅ Teleport NPC Command: new bypass solopvp for gatekeeper.
✅ Random Name Generator: Generates random names.
✅ PvP Flag: The players are flagged within this zone.
xml preview & java code
backup code -> https://pastebin.com/974V2p2p
SoloZone.xml
<?xml version="1.0" encoding="UTF-8"?>
<list>
<zone shape="NPoly" minZ="-5200" maxZ="-4680"><!-- Frintezza Solo Zone -->
<stat name="name" val="Solo PvP Zone" />
<stat name="locs" val="174244,-89089,-5112;174260,-86881,-5112;173184,-88090,-5112;175309,-88018,-5112;174231,-88019,-5112;175136,-88828,-5104;174962,-87025,-5104;173149,-87142,-5104;173470,-88908,-5112" />
<stat name="restrictedClasses" val="15,16,97" />
<node x="172031" y="-90127"/>
<node x="176428" y="-90089"/>
<node x="176428" y="-74051"/>
<node x="172057" y="-74108"/>
</zone>
</list>
SoloZone Code:
diff --git a/java/net/sf/l2j/gameserver/taskmanager/SoloZoneTaskManager.java b/java/net/sf/l2j/gameserver/taskmanager/SoloZoneTaskManager.java
new file mode 100644
index 0000000..6b7ef6f
--- /dev/null
+++ a/java/net/sf/l2j/gameserver/taskmanager/SoloZoneTaskManager.java
@@ -0,0 +1,98 @@
+package net.sf.l2j.gameserver.taskmanager;
+
+import java.security.SecureRandom;
+import java.util.ArrayList;
+import java.util.logging.Logger;
+
+import net.sf.l2j.commons.random.Rnd;
+
+import net.sf.l2j.gameserver.data.manager.ZoneManager;
+import net.sf.l2j.gameserver.enums.ZoneId;
+import net.sf.l2j.gameserver.handler.voicecommandhandlers.VoiceExitSoloZone;
+import net.sf.l2j.gameserver.model.World;
+import net.sf.l2j.gameserver.model.actor.Player;
+import net.sf.l2j.gameserver.model.location.Location;
+import net.sf.l2j.gameserver.model.zone.type.SoloZone;
+
+
+/**
+ * @author MarGaZeaS
+ */
+public class SoloZoneTaskManager implements Runnable {
+
+ private static final Location EXIT_LOCATION = VoiceExitSoloZone.getExitLocation(); // Λαμβάνουμε την έξοδο από το VoiceExitSoloZone
+
+ @Override
+ public void run()
+ {
+ // Διασχίζουμε όλους τους παίκτες του κόσμου
+ for (Player player : World.getInstance().getPlayers())
+ {
+ // Ελέγχουμε αν ο παίκτης είναι στο SoloZone
+ if (player.isInsideZone(ZoneId.SOLO))
+ {
+ // Μεταφέρουμε τον παίκτη στην έξοδο
+ player.teleportTo(EXIT_LOCATION.getX(), EXIT_LOCATION.getY(), EXIT_LOCATION.getZ(), 0);
+ player.sendMessage("The server is restarting, you have been moved out of the Solo Zone.");
+ }
+ }
+ }
+
+ private int _id;
+
+ private static final Logger _log = Logger.getLogger(SoloZoneTaskManager.class.getName());
+ private static final ArrayList<String> _rndNames = new ArrayList<>();
+ private static final int RANDOM_NAMES = 500;
+ private static final String CHARS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
+ private int _playersInSoloZone = 0;
+
+ public int getPlayersInside() {
+ return _playersInSoloZone;
+ }
+
+ public void setPlayersInside(int val) {
+ _playersInSoloZone = val;
+ }
+
+ public SoloZoneTaskManager() {
+ _log.info("Solo Zone System: Loading...");
+ for (int i = 0; i < RANDOM_NAMES; i++) {
+ String name = generateName();
+ _rndNames.add(name);
+ _log.info("Generated name: " + name);
+ }
+ _log.info("Solo Zone System: Loaded " + _rndNames.size() + " names.");
+ }
+
+ public String getAName() {
+ if (_rndNames.isEmpty()) {
+ _log.warning("SoloZoneManager: No random names available.");
+ return "Unknown";
+ }
+ return _rndNames.get(Rnd.get(5, RANDOM_NAMES - 5));
+ }
+
+ private static String generateName() {
+ SecureRandom rnd = new SecureRandom();
+ StringBuilder sb = new StringBuilder(15);
+ for (int i = 0; i < 15; i++) {
+ sb.append(CHARS.charAt(rnd.nextInt(CHARS.length())));
+ }
+ return sb.toString();
+ }
+
+ public int getZoneId()
+ {
+ return _id;
+ }
+
+ public final static SoloZone getCurrentZone() {
+ return ZoneManager.getInstance().getAllZones(SoloZone.class)
+ .stream()
+ .findFirst() // Επιστρέφει την πρώτη SoloZone (αν υπάρχει μόνο μία)
+ .orElse(null);
+ }
+
+ public static SoloZoneTaskManager getInstance() {
+ return SingletonHolder._instance;
+ }
+
+ private static class SingletonHolder {
+ private static final SoloZoneTaskManager _instance = new SoloZoneTaskManager();
+ }
+}
diff --git a/aCis_gameserver/java/net/sf/l2j/gameserver/taskmanager/PvpFlagTaskManager.java b/aCis_gameserver/java/net/sf/l2j/gameserver/taskmanager/PvpFlagTaskManager.java
index a707ce5..d247e2e 100644
--- a/aCis_gameserver/java/net/sf/l2j/gameserver/taskmanager/PvpFlagTaskManager.java
final Player player = entry.getKey();
final long timeLeft = entry.getValue();
+ if(player.isInsideZone(ZoneId.SOLO))
+ continue;
if(player.isInsideZone(ZoneId.BOSS))
continue;
// Time is running out, clear PvP flag and remove from list.
if (currentTime > timeLeft)
diff --git a/aCis_gameserver/java/net/sf/l2j/gameserver/network/clientpackets/RequestCharacterCreate.java b/aCis_gameserver/java/net/sf/l2j/gameserver/network/clientpackets/RequestCharacterCreate.java
index a707ce5..d247e2e 100644
+++ b/aCis_gameserver/java/net/sf/l2j/gameserver/network/clientpackets/RequestCharacterCreate.java
if (Config.ALLOW_FISH_CHAMPIONSHIP)
FishingChampionshipManager.getInstance();
+ if (Config.ENABLE_STARTUP)
+ StartupManager.getInstance();
diff --git a/java/net/sf/l2j/gameserver/handler/admincommandhandlers/AdminMaintenance.java b/java/net/sf/l2j/gameserver/handler/admincommandhandlers/AdminMaintenance.java
new file mode 100644
index 0000000..6b7ef6f
--- /dev/null
+++ a/java/net/sf/l2j/gameserver/handler/admincommandhandlers/AdminMaintenance.java
if (!st.hasMoreTokens())
{
sendHtmlForm(player);
return;
}
try
{
switch (st.nextToken())
{
case "shutdown":
+ SoloZoneTaskManager exitTask = new SoloZoneTaskManager();
+ ThreadPool.schedule(exitTask, 0);
Shutdown.getInstance().startShutdown(player, null, Integer.parseInt(st.nextToken()), false);
break;
case "restart":
+ exitTask = new SoloZoneTaskManager();
+ ThreadPool.schedule(exitTask, 0);
Shutdown.getInstance().startShutdown(player, null, Integer.parseInt(st.nextToken()), true);
break;
case "abort":
Shutdown.getInstance().abort(player);
break;
diff --git a/java/net/sf/l2j/gameserver/handler/voicecommandhandlers/VoiceExitSoloZone.java b/java/net/sf/l2j/gameserver/handler/voicecommandhandlers/VoiceExitSoloZone.java
new file mode 100644
index 0000000..6b7ef6f
--- /dev/null
+++ a/java/net/sf/l2j/gameserver/handler/voicecommandhandlers/VoiceExitSoloZone.java
+package net.sf.l2j.gameserver.handler.voicecommandhandlers;
+
+import net.sf.l2j.commons.pool.ThreadPool;
+
+import net.sf.l2j.gameserver.enums.ZoneId;
+import net.sf.l2j.gameserver.handler.IVoiceCommandHandler;
+import net.sf.l2j.gameserver.model.actor.Player;
+import net.sf.l2j.gameserver.model.location.Location;
+import net.sf.l2j.gameserver.network.serverpackets.MagicSkillUse;
+
+/**
+ * Handles the voice command for exiting the Solo Zone with delay and effects.
+ *
+ * @author MarGaZeaS
+ */
+public class VoiceExitSoloZone implements IVoiceCommandHandler
+{
+ private static final String[] VOICE_COMMANDS =
+ {
+ "exit"
+ };
+
+ // Default location to teleport players when exiting the Solo Zone
+ private static final Location EXIT_LOCATION = new Location(81318, 148064, -3464); // Replace with your desired coordinates
+
+ // Προσθήκη της μεθόδου για να πάρουμε την τοποθεσία εξόδου
+ public static Location getExitLocation() {
+ return EXIT_LOCATION;
+ }
+
+ @Override
+ public void useVoiceCommand(Player player, String command)
+ {
+ if (command.equalsIgnoreCase("exit"))
+ {
+ if (!player.isInsideZone(ZoneId.SOLO))
+ {
+ player.sendMessage("You are not inside the Solo Zone.");
+ return;
+ }
+
+ // Notify the player about the delay
+ player.sendMessage("You will be teleported out of the Solo Zone in 2 seconds.");
+
+ // Cast skill effect (Skill ID: 2100, Level: 1)
+ player.broadcastPacket(new MagicSkillUse(player, player, 2100, 1, 2000, 0));
+
+ // Schedule the teleportation after a 2-second delay
+ ThreadPool.schedule(() -> {
+ // Teleport the player to the designated exit location
+ player.teleportTo(EXIT_LOCATION.getX(), EXIT_LOCATION.getY(), EXIT_LOCATION.getZ(), 0);
+
+ // Inform the player
+ player.sendMessage("You have exited the Solo Zone.");
+ }, 2000); // Delay in milliseconds (2000ms = 2 seconds)
+ }
+ }
+
+ @Override
+ public String[] getVoiceCommandList()
+ {
+ return VOICE_COMMANDS;
+ }
+}
diff --git a/java/net/sf/l2j/gameserver/handler/VoiceCommandHandler.java b/java/net/sf/l2j/gameserver/handler/VoiceCommandHandler.java
new file mode 100644
index 0000000..6b7ef6f
--- /dev/null
+++ a/java/net/sf/l2j/gameserver/handler/VoiceCommandHandler.java
public class VoiceCommandHandler
{
private final Map<String, IVoiceCommandHandler> _entries = new HashMap<>();
protected VoiceCommandHandler()
{
............
............
+ registerHandler(new VoiceExitSoloZone());
}
public void registerHandler(IVoiceCommandHandler handler)
{
for (String command : handler.getVoiceCommandList())
_entries.put(command, handler);
}
diff --git a/java/net/sf/l2j/gameserver/model/actor/Npc.java b/java/net/sf/l2j/gameserver/model/actor/Npc.java
new file mode 100644
index 0000000..6b7ef6f
--- /dev/null
+++ a/java/net/sf/l2j/gameserver/model/actor/Npc.java
else if (command.startsWith("Chat"))
{
int val = 0;
try
{
val = Integer.parseInt(command.substring(5));
}
catch (final IndexOutOfBoundsException ioobe)
{
}
catch (final NumberFormatException nfe)
{
}
showChatWindow(player, val);
+ )
+ else if (command.startsWith("solopvp"))
+ {
+ SoloZoneTaskManager.getInstance();
+ player.teleportTo(SoloZoneTaskManager.getCurrentZone().getLoc(), 25);
+ }
else if (command.startsWith("Link"))
{
final String path = command.substring(5).trim();
if (path.indexOf("..") != -1)
return;
final NpcHtmlMessage html = new NpcHtmlMessage(getObjectId());
html.setFile("data/html/" + path);
html.replace("%objectId%", getObjectId());
player.sendPacket(html);
}
diff --git a/java/net/sf/l2j/gameserver/network/clientpackets/RequestRestartPoint.java b/java/net/sf/l2j/gameserver/network/clientpackets/RequestRestartPoint.java
new file mode 100644
index 0000000..6b7ef6f
--- /dev/null
+++ a/java/net/sf/l2j/gameserver/network/clientpackets/RequestRestartPoint.java
// Fixed.
- else if (_requestType == 4)
- {
- if (!player.isGM() && !player.isFestivalParticipant())
- return;
-
- loc = player.getPosition();
- }
+ if (_requestType == 4)
+ {
+ // Έλεγχος αν ο παίκτης δεν είναι GM, δεν είναι μέρος του φεστιβάλ και δεν είναι στην Solo Zone
+ if (!player.isGM() && !player.isFestivalParticipant() && !player.isInsideZone(ZoneId.SOLO))
+ {
+ return;
+ }
+
+ SoloZoneTaskManager.getInstance();
+ SoloZone currentZone = SoloZoneTaskManager.getCurrentZone();
+ if (currentZone != null && currentZone.getLoc() != null)
+ {
+ // Αν υπάρχει ζώνη και οι τοποθεσίες δεν είναι κενές, χρησιμοποιούμε τυχαία τοποθεσία από την ζώνη
+ loc = currentZone.getLoc();
+ } else
+ {
+ // Διαφορετικά, κάνουμε respawn στην τρέχουσα θέση του παίκτη
+ loc = player.getPosition();
+ }
+ }
diff --git a/java/net/sf/l2j/gameserver/network/clientpackets/RequestRestart.java b/java/net/sf/l2j/gameserver/network/clientpackets/RequestRestart.java
new file mode 100644
index 0000000..6b7ef6f
--- /dev/null
+++ a/java/net/sf/l2j/gameserver/network/clientpackets/RequestRestart.java
if (player.isFestivalParticipant() && FestivalOfDarknessManager.getInstance().isFestivalInitialized())
{
player.sendPacket(SystemMessageId.NO_RESTART_HERE);
sendPacket(RestartResponse.valueOf(false));
return;
}
+ if (player.isInsideZone(ZoneId.SOLO))
+ {
+ player.sendMessage("You cannot restart your character while in Solo Zone. Use .exit to leave");
+ player.setFakeName(null);
+ sendPacket(RestartResponse.valueOf(false));
+ return;
+ }
player.removeFromBossZone();
diff --git a/java/net/sf/l2j/gameserver/network/clientpackets/Logout.java b/java/net/sf/l2j/gameserver/network/clientpackets/Logout.java
new file mode 100644
index 0000000..6b7ef6f
--- /dev/null
+++ a/java/net/sf/l2j/gameserver/network/clientpackets/Logout.java
player.removeFromBossZone();
player.logout(true);
}
}
+
+ if (player.isInsideZone(ZoneId.SOLO))
+ {
+ player.sendMessage("You cannot logout or restart your character while in Solo Zone. Use .exit to leave");
+ player.setFakeName(null);
+ player.sendPacket(ActionFailed.STATIC_PACKET);
+ return;
+ }
+
player.removeFromBossZone();
player.logout(true);
}
}
diff --git a/java/net/sf/l2j/gameserver/model/zone/type/SoloZone.java b/java/net/sf/l2j/gameserver/model/zone/type/SoloZone.java
new file mode 100644
index 0000000..6b7ef6f
--- /dev/null
+++ a/java/net/sf/l2j/gameserver/model/zone/type/SoloZone.java
+package net.sf.l2j.gameserver.model.zone.type;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+import net.sf.l2j.commons.random.Rnd;
+
+import net.sf.l2j.Config;
+import net.sf.l2j.gameserver.enums.MessageType;
+import net.sf.l2j.gameserver.enums.ZoneId;
+import net.sf.l2j.gameserver.handler.voicecommandhandlers.VoiceExitSoloZone;
+import net.sf.l2j.gameserver.model.World;
+import net.sf.l2j.gameserver.model.actor.Creature;
+import net.sf.l2j.gameserver.model.actor.Player;
+import net.sf.l2j.gameserver.model.location.Location;
+import net.sf.l2j.gameserver.model.zone.type.subtype.ZoneType;
+import net.sf.l2j.gameserver.network.SystemMessageId;
+import net.sf.l2j.gameserver.network.serverpackets.EtcStatusUpdate;
+import net.sf.l2j.gameserver.taskmanager.PvpFlagTaskManager;
+import net.sf.l2j.gameserver.taskmanager.SoloZoneTaskManager;
+
+/**
+ * @author MarGaZeaS
+ *
+ */
+public class SoloZone extends ZoneType
+{
+ private String _name;
+ private List<Location> _locations = new ArrayList<>();
+
+ public SoloZone(int id)
+ {
+ super(id);
+ }
+
+ @Override
+ public void setParameter(String name, String value)
+ {
+ if (name.equals("name"))
+ _name = value;
+ else if (name.equals("locs"))
+ {
+ for (String locs : value.split(";"))
+ {
+ String[] coordinates = locs.split(",");
+ if (coordinates.length == 3)
+ {
+ int x = Integer.parseInt(coordinates[0]);
+ int y = Integer.parseInt(coordinates[1]);
+ int z = Integer.parseInt(coordinates[2]);
+ _locations.add(new Location(x, y, z));
+ }
+ else
+ {
+ LOGGER.warn("Invalid location format: " + locs);
+ }
+ }
+ }
+ }
+
+
+ @Override
+ protected void onEnter(Creature character)
+ {
+ if (character instanceof Player)
+ {
+ final Player player = (Player) character;
+
+ if ((player.getClassId().getId() == 15 || player.getClassId().getId() == 16 || player.getClassId().getId() == 97))
+ {
+ Location respawnLocation = VoiceExitSoloZone.getExitLocation();
+ player.instantTeleportTo(respawnLocation, 20);
+ player.sendMessage("Your class is not allowed in this zone.");
+ return;
+ }
+
+ String randomName = SoloZoneTaskManager.getInstance().getAName();
+ if (randomName == null || randomName.isEmpty() || !isValidName(randomName))
+ {
+ randomName = generateRandomName();
+ }
+ if (isNameAlreadyTaken(randomName))
+ {
+ randomName = generateRandomName();
+ }
+ player.setFakeName(randomName);
+ player.sendMessage("Welcome to the Solo Zone, your random name is: " + randomName);
+ player.sendPacket(SystemMessageId.ENTERED_COMBAT_ZONE);
+ character.setInsideZone(ZoneId.SOLO, true);
+ character.setInsideZone(ZoneId.NO_STORE, true);
+ character.setInsideZone(ZoneId.NO_SUMMON_FRIEND, true);
+
+ if (player.getParty() != null)
+ {
+ player.getParty().removePartyMember(player, MessageType.DISCONNECTED);
+ }
+
+ if (player.getPvpFlag() > 0)
+ PvpFlagTaskManager.getInstance().remove(player, true);
+
+ player.updatePvPStatus();
+ player.broadcastUserInfo();
+ }
+ }
+
+ private static boolean isValidName(String name) {
+ return name.matches("[a-zA-Z0-9_]+");
+ }
+
+ private static String generateRandomName() {
+ Random rand = new Random();
+ int nameLength = rand.nextInt(12) + 4;
+ StringBuilder nameBuilder = new StringBuilder();
+
+ for (int i = 0; i < nameLength; i++) {
+ char randomChar = (char) (rand.nextInt(26) + 'a');
+ nameBuilder.append(randomChar);
+ }
+
+ return nameBuilder.toString();
+ }
+
+ private static boolean isNameAlreadyTaken(String name) {
+ return World.getInstance().getPlayers().stream().anyMatch(player -> player.getFakeName().equals(name));
+ }
+
+ @Override
+ protected void onExit(Creature character)
+ {
+ character.setInsideZone(ZoneId.SOLO, false); // Solo zone
+ character.setInsideZone(ZoneId.NO_STORE, false); // Allow making a store
+ character.setInsideZone(ZoneId.NO_SUMMON_FRIEND, false); // Allow summon
+
+ if (character instanceof Player)
+ {
+ final Player player = (Player) character;
+
+ if (player.getFakeName() != null)
+ {
+ player.setFakeName(null);
+ }
+
+ player.sendPacket(SystemMessageId.LEFT_COMBAT_ZONE);
+ {
+ if(!player.isInObserverMode() && player.getPvpFlag() > 0)
+ PvpFlagTaskManager.getInstance().add(player, Config.PVP_NORMAL_TIME);
+
+ player.sendPacket(new EtcStatusUpdate(player));
+ player.broadcastUserInfo();
+ }
+ }
+ }
+
+ public String getName()
+ {
+ return _name;
+ }
+
+ public Location getLoc()
+ {
+ if (_locations.isEmpty())
+ {
+ return null; // Αν η λίστα είναι κενή, επιστρέφουμε null
+ }
+ return _locations.get(Rnd.get(0, _locations.size() - 1)); // Επιλέγουμε τυχαία τοποθεσία
+ }
+}
diff --git a/java/net/sf/l2j/gameserver/GameServer.java b/java/net/sf/l2j/gameserver/GameServer.java
new file mode 100644
index 0000000..6b7ef6f
--- /dev/null
+++ a/java/net/sf/l2j/gameserver/GameServer.java
if (Config.ALLOW_FISH_CHAMPIONSHIP)
FishingChampionshipManager.getInstance();
+ StringUtil.printSection("Custom Features");
+ SoloZoneTaskManager.getInstance();
StringUtil.printSection("Handlers");
LOGGER.info("Loaded {} admin command handlers.", AdminCommandHandler.getInstance().size());
diff --git a/java/net/sf/l2j/gameserver/GameServer.java b/java/net/sf/l2j/gameserver/Shutdown.java
new file mode 100644
index 0000000..6b7ef6f
--- /dev/null
+++ a/java/net/sf/l2j/gameserver/Shutdown.java
// disconnect players
try
{
disconnectAllPlayers();
LOGGER.info("All players have been disconnected.");
}
catch (Exception e)
{
// Silent catch.
}
+ // Restore real names for players in SoloZone
+ restoreRealNamesInSoloZone();
// stop all threadpolls
ThreadPool.shutdown();
try
{
LoginServerThread.getInstance().interrupt();
}
catch (Exception e)
{
// Silent catch.
}
// avoids new players from logging in
if (_secondsShut <= 60 && LoginServerThread.getInstance().getServerType() != ServerType.DOWN)
LoginServerThread.getInstance().setServerType(ServerType.DOWN);
_secondsShut--;
Thread.sleep(1000);
}
}
catch (InterruptedException e)
{
}
}
+ // This method restores the real names of players in SoloZone
+ private static void restoreRealNamesInSoloZone()
+ {
+ for (Player player : World.getInstance().getPlayers())
+ {
+ // Check if player is inside the SoloZone
+ if (player.isInsideZone(ZoneId.SOLO))
+ {
+ // Restore the real name by removing the fake name
+ if (player.getFakeName() != null)
+ {
+ player.setFakeName(null); // Restore the real name
+ LOGGER.info("Player {}'s fake name has been removed and real name restored.", player.getName());
+ }
+ }
+ }
+ }
private static void sendServerQuit(int seconds)
{
World.toAllOnlinePlayers(SystemMessage.getSystemMessage(SystemMessageId.THE_SERVER_WILL_BE_COMING_DOWN_IN_S1_SECONDS).addNumber(seconds));
}
diff --git a/java/net/sf/l2j/gameserver/enums/ZoneId.java b/java/net/sf/l2j/gameserver/enums/ZoneId.java
new file mode 100644
index 0000000..6b7ef6f
--- /dev/null
+++ a/java/net/sf/l2j/gameserver/enums/ZoneId.java
public enum ZoneId
{
PVP(0),
PEACE(1),
SIEGE(2),
MOTHER_TREE(3),
CLAN_HALL(4),
NO_LANDING(5),
WATER(6),
JAIL(7),
MONSTER_TRACK(8),
CASTLE(9),
SWAMP(10),
NO_SUMMON_FRIEND(11),
NO_STORE(12),
TOWN(13),
HQ(14),
DANGER_AREA(15),
CAST_ON_ARTIFACT(16),
NO_RESTART(17),
SCRIPT(18),
- BOSS(19),
+ BOSS(19),
+ SOLO(20);
private final int _id;
private ZoneId(int id)
{
_id = id;
}
diff --git a/java/net/sf/l2j/gameserver/network/serverpackets/Die.java b/java/net/sf/l2j/gameserver/network/serverpackets/Die.java
new file mode 100644
index 0000000..6b7ef6f
--- /dev/null
+++ a/java/net/sf/l2j/gameserver/network/serverpackets/Die.java
if (creature instanceof Player)
{
Player player = (Player) creature;
- _allowFixedRes = player.getAccessLevel().allowFixedRes();
+ _allowFixedRes = player.getAccessLevel().allowFixedRes() || player.isInsideZone(ZoneId.SOLO);
_clan = player.getClan();
}
diff --git a/java/net/sf/l2j/gameserver/model/actor/Player.java b/java/net/sf/l2j/gameserver//model/actor/Player.java
new file mode 100644
index 0000000..6b7ef6f
--- /dev/null
+++ a/java/net/sf/l2j/gameserver/network/clientpackets/EnterWorld.java
// Attacker or spectator logging into a siege zone will be ported at town.
if (player.isInsideZone(ZoneId.SIEGE) && player.getSiegeState() < 2)
player.teleportTo(TeleportType.TOWN);
+ if (player.isInsideZone(ZoneId.SOLO))
+ {
+ ThreadPool.schedule(() -> {
+ Location exitLocation = VoiceExitSoloZone.getExitLocation();
+
+ if (exitLocation != null)
+ {
+ player.teleportTo(exitLocation.getX(), exitLocation.getY(), exitLocation.getZ(), 0);
+ player.sendMessage("You have been moved to the exit of the SoloZone.");
+ }
+ }, 5000); // 5000 milliseconds (5sec)
+ }
diff --git a/java/net/sf/l2j/gameserver/model/actor/Player.java b/java/net/sf/l2j/gameserver/model/actor/Player.java
new file mode 100644
index 0000000..6b7ef6f
--- /dev/null
+++ a/java/net/sf/l2j/gameserver/model/actor/Player.java
@Override
public void doRevive()
{
super.doRevive();
stopEffects(EffectType.CHARM_OF_COURAGE);
sendPacket(new EtcStatusUpdate(this));
getStatus().setCpHpMp(getStatus().getMaxCp(), getStatus().getMaxHp(), getStatus().getMaxMp());
_reviveRequested = 0;
_revivePower = 0;
if (isMounted())
startFeed(_mountNpcId);
+ if (isInsideZone(ZoneId.SOLO))
+ {
+ // Give Nobless (1323 ID)
+ L2Skill no = SkillTable.getInstance().getInfo(1323, 1);
+ no.getEffects(this, this);
+ sendMessage("You have received the Nobless status in the Solo Zone.");
+ }
+ }
If anyone thinks the code is wrong, please make an update and upload it here so I can update the post. A part was edited with chatgpt