Compare commits
26 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d5be9130aa | ||
|
|
b5871c7be2 | ||
|
|
f9b328b432 | ||
|
|
830df4e5ca | ||
|
|
6f68f4edfd | ||
|
|
a7931c3e4d | ||
|
|
799cb97564 | ||
|
|
851b57e8ef | ||
|
|
ceb2866aa1 | ||
|
|
11c1e4655e | ||
|
|
aca9cbf91d | ||
|
|
08b089d9be | ||
|
|
8b254a294e | ||
|
|
7004de6e71 | ||
|
|
738e1b24e6 | ||
|
|
5a4200cc8e | ||
|
|
cb0320a471 | ||
|
|
058f034c2b | ||
|
|
9c54f3a8ea | ||
|
|
ee8cb545e1 | ||
|
|
64bbff462d | ||
|
|
f63f7e13d4 | ||
|
|
db8666fc71 | ||
|
|
93ea66e6fe | ||
|
|
7131e39c96 | ||
|
|
b80e9a3310 |
24
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
24
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description (in English) of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
@@ -347,7 +347,6 @@ server:
|
||||
USE_PERFECT_SCROLLING: false #Scrolls doesn't use slots upon failure.
|
||||
USE_ENHANCED_CHSCROLL: false #Equips even more powerful with chaos upgrade.
|
||||
USE_ENHANCED_CRAFTING: false #Apply chaos scroll on every equip crafted.
|
||||
USE_ENHANCED_CLNSLATE: false #Clean slates can be applied to recover successfully used slots as well.
|
||||
SCROLL_CHANCE_ROLLS: 1 #Number of rolls for success on a scroll, set 1 for default.
|
||||
CHSCROLL_STAT_RATE: 1 #Number of rolls of stat upgrade on a successfully applied chaos scroll, set 1 for default.
|
||||
CHSCROLL_STAT_RANGE: 6 #Stat upgrade range (-N, N) on chaos scrolls.
|
||||
|
||||
@@ -31,7 +31,7 @@ function action(mode, type, selection) {
|
||||
}
|
||||
|
||||
if (status == 0) {
|
||||
cm.sendSimple("Hey, you look like you need a breather. You should be enjoying the life, just like I am. Well, if you have a couple of items, I can trade you for an item you can play minigames with. Now... what can I do for you?#b\r\n#L0#Create a minigame item#l\r\n#L1#Explain to me what the minigames are about#l#k");
|
||||
cm.sendSimple("Hey, you look like you need a breather from all that hunting. You should be enjoying the life, just like I am. Well, if you have a couple of items, I can trade you for an item you can play minigames with. Now... what can I do for you?#b\r\n#L0#Create a minigame item#l\r\n#L1#Explain to me what the minigames are about#l#k");
|
||||
|
||||
} else if (status == 1) {
|
||||
if (selection == 0) {
|
||||
@@ -57,6 +57,7 @@ function action(mode, type, selection) {
|
||||
if (cm.haveItem(4030012, 15)) {
|
||||
cm.gainItem(4030012, -15);
|
||||
cm.gainItem(4080100, 1);
|
||||
cm.dispose();
|
||||
} else {
|
||||
cm.sendNext("You want #bA set of Match Cards#k? Hmm...to make A set of Match Cards, you'll need some #bMonster Cards#k. Monster Card can be obtained by taking out the monsters all around the island. Collect 15 Monster Cards and you can make a set of A set of Match Cards."); //Lmfao a set of A set xD
|
||||
cm.dispose();
|
||||
@@ -81,7 +82,7 @@ function action(mode, type, selection) {
|
||||
if (current == 1 || current == 2) {
|
||||
cm.sendNextPrev("Enter the room, and when you're ready to play, click on #bReady#k.\r\nOnce the visitor clicks on #bReady#k, the room owner can press #bStart#k to begin the game. If an unwanted visitor walks in, and you don't want to play with that person, the room owner has the right to kick the visitor out of the room. There will be a square box with x written on the right of that person. Click on that for a cold goodbye, okay?"); //Oh yeah, because people WALK in Omok Rooms.
|
||||
} else if (current == 3) {
|
||||
if (cm.haveItem(omok1piece[selection], 99) && cm.haveItem(omok2piece[selection], 99) && cm.haveItem(4030009, 1)) {
|
||||
if (cm.haveItem(omok1piece[selection], omokamount) && cm.haveItem(omok2piece[selection], omokamount) && cm.haveItem(4030009, 1)) {
|
||||
cm.gainItem(omok1piece[selection], -omokamount);
|
||||
cm.gainItem(omok2piece[selection], -omokamount);
|
||||
cm.gainItem(4030009, -1);
|
||||
@@ -95,7 +96,7 @@ function action(mode, type, selection) {
|
||||
|
||||
} else if (status == 5) {
|
||||
if (current == 1) {
|
||||
cm.sendNextPrev("When the first fame starts, #bthe room owner goes first#k. Beward that you'll be given a time limit, and you may lose your turn if you don't make your move on time. Normally, 3 x 3 is not allowed, but if there comes a point that it's absolutely necessary to put your piece there or face ending the game, then you can put it there. 3 x 3 is allowed as the last line of defense! Oh, and it won't count if it's #r6 or 7 straight#k. Only 5!");
|
||||
cm.sendNextPrev("When the first game starts, #bthe room owner goes first#k. Beward that you'll be given a time limit, and you may lose your turn if you don't make your move on time. Normally, 3 x 3 is not allowed, but if there comes a point that it's absolutely necessary to put your piece there or face ending the game, then you can put it there. 3 x 3 is allowed as the last line of defense! Oh, and it won't count if it's #r6 or 7 straight#k. Only 5!");
|
||||
} else if (current == 2) {
|
||||
cm.sendNextPrev("Oh, and unlike Omok, when you create the game room for Match Cards, you'll need to set your game on the number of cards you'll use for the game. There are 3 modes avaliable, 3x4, 4x5, and 5x6, which will require 12, 20, and 30 cards respectively. Remember that you won't beable to change it up once the room is open, so if you really wish to change it up, you may have to close the room and open another one.");
|
||||
}
|
||||
@@ -110,12 +111,14 @@ function action(mode, type, selection) {
|
||||
} else if (status == 7) {
|
||||
if (current == 1) {
|
||||
cm.sendPrev("When the next game starts, the loser will go first. Also, no one is allowed to leave in the middle of a game. If you do, you may need to request either a #bforfeit or tie#k. (Of course, if you request a forfeit, you'll lose the game.) And if you click on 'Leave' in the middle of the game and call to leave after the game, you'll leave the room right after the game is over. This will be a much more useful way to leave.");
|
||||
cm.dispose();
|
||||
} else if (current == 2) {
|
||||
cm.sendNextPrev("If you and your opponent have the same number of matched pairs, then whoever had a longer streak of matched pairs will win. If you ever feel the need to go to the bathroom, or take an extended break, you can request a #btie#k. The game will end in a tie if the opponent accepts the request. Tip: this may be a good way to keep your friendships in tact.");
|
||||
}
|
||||
} else if (status == 8) {
|
||||
if (current == 2) {
|
||||
cm.sendPrev("When the next game starts, the loser will go first. Also, no one is allowed to leave in the middle of a game. If you do, you may need to request either a #bforfeit or tie#k. (Of course, if you request a forfeit, you'll lose the game.) And if you click on 'Leave' in the middle of the game and call to leave after the game, you'll leave the room right after the game is over. This will be a much more useful way to leave.");
|
||||
cm.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,12 @@ function action(mode, type, selection) {
|
||||
}
|
||||
|
||||
if (!cm.isEventLeader()) {
|
||||
cm.sendYesNo("I wish for your leader to talk to me. Alternatively, you may be wanting to quit. Are you going to abandon this campaign?");
|
||||
// Player chose "No" or "End Chat"
|
||||
if (mode <= 0) {
|
||||
cm.dispose();
|
||||
} else {
|
||||
cm.sendYesNo("I wish for your leader to talk to me. Alternatively, you may be wanting to quit. Are you going to abandon this campaign?");
|
||||
}
|
||||
} else {
|
||||
var eim = cm.getEventInstance();
|
||||
if (eim == null) {
|
||||
|
||||
@@ -47,19 +47,19 @@ public abstract class CharacterFactory {
|
||||
return -1;
|
||||
}
|
||||
|
||||
Character newchar = Character.getDefault(c);
|
||||
newchar.setWorld(c.getWorld());
|
||||
newchar.setSkinColor(SkinColor.getById(skin));
|
||||
newchar.setGender(gender);
|
||||
newchar.setName(name);
|
||||
newchar.setHair(hair);
|
||||
newchar.setFace(face);
|
||||
Character newCharacter = Character.getDefault(c);
|
||||
newCharacter.setWorld(c.getWorld());
|
||||
newCharacter.setSkinColor(SkinColor.getById(skin));
|
||||
newCharacter.setGender(gender);
|
||||
newCharacter.setName(name);
|
||||
newCharacter.setHair(hair);
|
||||
newCharacter.setFace(face);
|
||||
|
||||
newchar.setLevel(recipe.getLevel());
|
||||
newchar.setJob(recipe.getJob());
|
||||
newchar.setMapId(recipe.getMap());
|
||||
newCharacter.setLevel(recipe.getLevel());
|
||||
newCharacter.setJob(recipe.getJob());
|
||||
newCharacter.setMapId(recipe.getMap());
|
||||
|
||||
Inventory equipped = newchar.getInventory(InventoryType.EQUIPPED);
|
||||
Inventory equipped = newCharacter.getInventory(InventoryType.EQUIPPED);
|
||||
ItemInformationProvider ii = ItemInformationProvider.getInstance();
|
||||
|
||||
int top = recipe.getTop(), bottom = recipe.getBottom(), shoes = recipe.getShoes(), weapon = recipe.getWeapon();
|
||||
@@ -88,12 +88,17 @@ public abstract class CharacterFactory {
|
||||
equipped.addItemFromDB(eq_weapon.copy());
|
||||
}
|
||||
|
||||
if (!newchar.insertNewChar(recipe)) {
|
||||
if (!MakeCharInfoValidator.isNewCharacterValid(newCharacter)) {
|
||||
log.warn("Owner from account {} tried to packet edit in character creation", c.getAccountName());
|
||||
return -2;
|
||||
}
|
||||
c.sendPacket(PacketCreator.addNewCharEntry(newchar));
|
||||
|
||||
Server.getInstance().createCharacterEntry(newchar);
|
||||
if (!newCharacter.insertNewChar(recipe)) {
|
||||
return -2;
|
||||
}
|
||||
c.sendPacket(PacketCreator.addNewCharEntry(newCharacter));
|
||||
|
||||
Server.getInstance().createCharacterEntry(newCharacter);
|
||||
Server.getInstance().broadcastGMMessage(c.getWorld(), PacketCreator.sendYellowTip("[New Char]: " + c.getAccountName() + " has created a new character with IGN " + name));
|
||||
log.info("Account {} created chr with name {}", c.getAccountName(), name);
|
||||
|
||||
|
||||
140
src/main/java/client/creator/MakeCharInfo.java
Normal file
140
src/main/java/client/creator/MakeCharInfo.java
Normal file
@@ -0,0 +1,140 @@
|
||||
package client.creator;
|
||||
|
||||
import client.Character;
|
||||
import client.Job;
|
||||
import client.inventory.InventoryType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import provider.Data;
|
||||
import provider.DataTool;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public class MakeCharInfo {
|
||||
private static final Logger log = LoggerFactory.getLogger(MakeCharInfo.class);
|
||||
private static final String FACE_ID = "0";
|
||||
private static final String HAIR_ID = "1";
|
||||
private static final String HAIR_COLOR_ID = "2";
|
||||
private static final String SKIN_ID = "3";
|
||||
private static final String TOP_ID = "4";
|
||||
private static final String BOTTOM_ID = "5";
|
||||
private static final String SHOE_ID = "6";
|
||||
private static final String WEAPON_ID = "7";
|
||||
|
||||
private final Set<Integer> charFaces = new HashSet<>();
|
||||
private final Set<Integer> charHairs = new HashSet<>();
|
||||
private final Set<Integer> charHairColors = new HashSet<>();
|
||||
private final Set<Integer> charSkins = new HashSet<>();
|
||||
private final Set<Integer> charTops = new HashSet<>();
|
||||
private final Set<Integer> charBottoms = new HashSet<>();
|
||||
private final Set<Integer> charShoes = new HashSet<>();
|
||||
private final Set<Integer> charWeapons = new HashSet<>();
|
||||
|
||||
public MakeCharInfo(Data charInfoData) {
|
||||
for (Data data : charInfoData.getChildren()) {
|
||||
switch (data.getName()) {
|
||||
case FACE_ID -> {
|
||||
for (Data faceData : data) {
|
||||
charFaces.add(DataTool.getInt(faceData));
|
||||
}
|
||||
}
|
||||
case HAIR_ID -> {
|
||||
for (Data hairData : data) {
|
||||
charHairs.add(DataTool.getInt(hairData));
|
||||
}
|
||||
}
|
||||
case HAIR_COLOR_ID -> {
|
||||
for (Data hairColorData : data) {
|
||||
charHairColors.add(DataTool.getInt(hairColorData));
|
||||
}
|
||||
}
|
||||
case SKIN_ID -> {
|
||||
for (Data skinData : data) {
|
||||
charSkins.add(DataTool.getInt(skinData));
|
||||
}
|
||||
}
|
||||
case TOP_ID -> {
|
||||
for (Data topData : data) {
|
||||
charTops.add(DataTool.getInt(topData));
|
||||
}
|
||||
}
|
||||
case BOTTOM_ID -> {
|
||||
for (Data bottomData : data) {
|
||||
charBottoms.add(DataTool.getInt(bottomData));
|
||||
}
|
||||
}
|
||||
case SHOE_ID -> {
|
||||
for (Data shoeData : data) {
|
||||
charShoes.add(DataTool.getInt(shoeData));
|
||||
}
|
||||
}
|
||||
case WEAPON_ID -> {
|
||||
for (Data weaponData : data) {
|
||||
charWeapons.add(DataTool.getInt(weaponData));
|
||||
}
|
||||
}
|
||||
default -> log.error("Unhandled node inside MakeCharInfo.img.xml: '" + data.getName() + "'");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean verifyFaceId(int id) {
|
||||
return this.charFaces.contains(id);
|
||||
}
|
||||
|
||||
public boolean verifyHairId(int id) {
|
||||
if (id % 10 != 0) {
|
||||
return this.charHairs.contains(id - (id % 10));
|
||||
}
|
||||
return this.charHairs.contains(id);
|
||||
}
|
||||
|
||||
public boolean verifyHairColorId(int id) {
|
||||
return this.charHairColors.contains(id % 10);
|
||||
}
|
||||
|
||||
public boolean verifySkinId(int id) {
|
||||
return this.charSkins.contains(id);
|
||||
}
|
||||
|
||||
public boolean verifyTopId(int id) {
|
||||
return this.charTops.contains(id);
|
||||
}
|
||||
|
||||
public boolean verifyBottomId(int id) {
|
||||
return this.charBottoms.contains(id);
|
||||
}
|
||||
|
||||
public boolean verifyShoeId(int id) {
|
||||
return this.charShoes.contains(id);
|
||||
}
|
||||
|
||||
public boolean verifyWeaponId(int id) {
|
||||
return this.charWeapons.contains(id);
|
||||
}
|
||||
|
||||
public boolean verifyCharacter(Character character) {
|
||||
if (!verifyFaceId(character.getFace())) return false;
|
||||
if (!verifyHairId(character.getHair())) return false;
|
||||
if (!verifyHairColorId(character.getHair())) return false;
|
||||
if (!verifySkinId(character.getSkinColor().getId())) return false;
|
||||
|
||||
// Here we only verify the equipment if the character that's being created is of type 'Beginner'
|
||||
// This is because when the Maple Life A or Maple Life B items are used, the client does not send any data
|
||||
// regarding what equipment the character should be wearing (as it's all handled server-side)
|
||||
Job characterJob = character.getJob();
|
||||
if (characterJob == Job.BEGINNER || characterJob == Job.NOBLESSE || characterJob == Job.LEGEND) {
|
||||
if (!verifyTopId(character.getInventory(InventoryType.EQUIPPED).getItem((short) -5).getItemId()))
|
||||
return false;
|
||||
if (!verifyBottomId(character.getInventory(InventoryType.EQUIPPED).getItem((short) -6).getItemId()))
|
||||
return false;
|
||||
if (!verifyShoeId(character.getInventory(InventoryType.EQUIPPED).getItem((short) -7).getItemId()))
|
||||
return false;
|
||||
if (!verifyWeaponId(character.getInventory(InventoryType.EQUIPPED).getItem((short) -11).getItemId()))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
41
src/main/java/client/creator/MakeCharInfoValidator.java
Normal file
41
src/main/java/client/creator/MakeCharInfoValidator.java
Normal file
@@ -0,0 +1,41 @@
|
||||
package client.creator;
|
||||
|
||||
import client.Character;
|
||||
import provider.Data;
|
||||
import provider.DataProviderFactory;
|
||||
import provider.wz.WZFiles;
|
||||
|
||||
public class MakeCharInfoValidator {
|
||||
private static final MakeCharInfo charFemale;
|
||||
private static final MakeCharInfo charMale;
|
||||
private static final MakeCharInfo orientCharFemale;
|
||||
private static final MakeCharInfo orientCharMale;
|
||||
private static final MakeCharInfo premiumCharFemale;
|
||||
private static final MakeCharInfo premiumCharMale;
|
||||
|
||||
static {
|
||||
Data data = DataProviderFactory.getDataProvider(WZFiles.ETC).getData("MakeCharInfo.img");
|
||||
charFemale = new MakeCharInfo(data.getChildByPath("Info/CharFemale"));
|
||||
charMale = new MakeCharInfo(data.getChildByPath("Info/CharMale"));
|
||||
orientCharFemale = new MakeCharInfo(data.getChildByPath("OrientCharFemale"));
|
||||
orientCharMale = new MakeCharInfo(data.getChildByPath("OrientCharMale"));
|
||||
premiumCharFemale = new MakeCharInfo(data.getChildByPath("PremiumCharFemale"));
|
||||
premiumCharMale = new MakeCharInfo(data.getChildByPath("PremiumCharMale"));
|
||||
}
|
||||
|
||||
private static MakeCharInfo getMakeCharInfo(Character character) {
|
||||
return switch (character.getJob()) {
|
||||
case BEGINNER, WARRIOR, MAGICIAN, BOWMAN, THIEF, PIRATE -> character.isMale() ? charMale : charFemale;
|
||||
case NOBLESSE -> character.isMale() ? premiumCharMale : premiumCharFemale;
|
||||
case LEGEND -> character.isMale() ? orientCharMale : orientCharFemale;
|
||||
default -> null;
|
||||
};
|
||||
}
|
||||
|
||||
public static boolean isNewCharacterValid(Character character) {
|
||||
MakeCharInfo makeCharInfo = getMakeCharInfo(character);
|
||||
if (makeCharInfo == null) return false;
|
||||
|
||||
return makeCharInfo.verifyCharacter(character);
|
||||
}
|
||||
}
|
||||
@@ -43,7 +43,6 @@ public class BeginnerCreator extends CharacterFactory {
|
||||
}
|
||||
|
||||
public static int createCharacter(Client c, String name, int face, int hair, int skin, int top, int bottom, int shoes, int weapon, int gender) {
|
||||
int status = createNewCharacter(c, name, face, hair, skin, gender, createRecipe(Job.BEGINNER, 1, MapId.MUSHROOM_TOWN, top, bottom, shoes, weapon));
|
||||
return status;
|
||||
return createNewCharacter(c, name, face, hair, skin, gender, createRecipe(Job.BEGINNER, 1, MapId.MUSHROOM_TOWN, top, bottom, shoes, weapon));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,6 @@ public class LegendCreator extends CharacterFactory {
|
||||
}
|
||||
|
||||
public static int createCharacter(Client c, String name, int face, int hair, int skin, int top, int bottom, int shoes, int weapon, int gender) {
|
||||
int status = createNewCharacter(c, name, face, hair, skin, gender, createRecipe(Job.LEGEND, 1, MapId.ARAN_TUTORIAL_START, top, bottom, shoes, weapon));
|
||||
return status;
|
||||
return createNewCharacter(c, name, face, hair, skin, gender, createRecipe(Job.LEGEND, 1, MapId.ARAN_TUTORIAL_START, top, bottom, shoes, weapon));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,6 @@ public class NoblesseCreator extends CharacterFactory {
|
||||
}
|
||||
|
||||
public static int createCharacter(Client c, String name, int face, int hair, int skin, int top, int bottom, int shoes, int weapon, int gender) {
|
||||
int status = createNewCharacter(c, name, face, hair, skin, gender, createRecipe(Job.NOBLESSE, 1, MapId.STARTING_MAP_NOBLESSE, top, bottom, shoes, weapon));
|
||||
return status;
|
||||
return createNewCharacter(c, name, face, hair, skin, gender, createRecipe(Job.NOBLESSE, 1, MapId.STARTING_MAP_NOBLESSE, top, bottom, shoes, weapon));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -195,7 +195,6 @@ public class ServerConfig {
|
||||
public boolean USE_PERFECT_SCROLLING;
|
||||
public boolean USE_ENHANCED_CHSCROLL;
|
||||
public boolean USE_ENHANCED_CRAFTING;
|
||||
public boolean USE_ENHANCED_CLNSLATE;
|
||||
public int SCROLL_CHANCE_ROLLS;
|
||||
public int CHSCROLL_STAT_RATE;
|
||||
public int CHSCROLL_STAT_RANGE;
|
||||
|
||||
@@ -95,46 +95,6 @@ public class ItemId {
|
||||
public static final int BEGINNERS_GUIDE = 4161001;
|
||||
public static final int LEGENDS_GUIDE = 4161048;
|
||||
public static final int NOBLESSE_GUIDE = 4161047;
|
||||
public static final int SWORD = 1302000; // Weapon
|
||||
public static final int HAND_AXE = 1312004;
|
||||
public static final int WOODEN_CLUB = 1322005;
|
||||
public static final int BASIC_POLEARM = 1442079;
|
||||
public static final int WHITE_UNDERSHIRT = 1040002; // Top
|
||||
public static final int UNDERSHIRT = 1040006;
|
||||
public static final int GREY_TSHIRT = 1040010;
|
||||
public static final int WHITE_TUBETOP = 1041002;
|
||||
public static final int YELLOW_TSHIRT = 1041006;
|
||||
public static final int GREEN_TSHIRT = 1041010;
|
||||
public static final int RED_STRIPED_TOP = 1041011;
|
||||
public static final int SIMPLE_WARRIOR_TOP = 1042167;
|
||||
public static final int BLUE_JEAN_SHORTS = 1060002; // Bottom
|
||||
public static final int BROWN_COTTON_SHORTS = 1060006;
|
||||
public static final int RED_MINISKIRT = 1061002;
|
||||
public static final int INDIGO_MINISKIRT = 1061008;
|
||||
public static final int SIMPLE_WARRIOR_PANTS = 1062115;
|
||||
public static final int RED_RUBBER_BOOTS = 1072001;
|
||||
public static final int LEATHER_SANDALS = 1072005;
|
||||
public static final int YELLOW_RUBBER_BOOTS = 1072037;
|
||||
public static final int BLUE_RUBBER_BOOTS = 1072038;
|
||||
public static final int AVERAGE_MUSASHI_SHOES = 1072383;
|
||||
public static final int BLACK_TOBEN = 30000; // Hair
|
||||
public static final int ZETA = 30010;
|
||||
public static final int BLACK_REBEL = 30020;
|
||||
public static final int BLACK_BUZZ = 30030;
|
||||
public static final int BLACK_SAMMY = 31000;
|
||||
public static final int BLACK_EDGY = 31040;
|
||||
public static final int BLACK_CONNIE = 31050;
|
||||
public static final int MOTIVATED_LOOK_M = 20000; // Face
|
||||
public static final int PERPLEXED_STARE = 20001;
|
||||
public static final int LEISURE_LOOK_M = 20002;
|
||||
public static final int MOTIVATED_LOOK_F = 21000;
|
||||
public static final int FEARFUL_STARE_M = 21001;
|
||||
public static final int LEISURE_LOOK_F = 21002;
|
||||
public static final int FEARFUL_STARE_F = 21201;
|
||||
public static final int PERPLEXED_STARE_HAZEL = 20401;
|
||||
public static final int LEISURE_LOOK_HAZEL = 20402;
|
||||
public static final int MOTIVATED_LOOK_AMETHYST = 21700;
|
||||
public static final int MOTIVATED_LOOK_BLUE = 20100;
|
||||
|
||||
// Warrior
|
||||
public static final int RED_HWARANG_SHIRT = 1040021;
|
||||
|
||||
@@ -22,6 +22,8 @@ public class ByteBufInPacket implements InPacket {
|
||||
public byte readByte() {
|
||||
return byteBuf.readByte();
|
||||
}
|
||||
@Override
|
||||
public short readUnsignedByte() { return byteBuf.readUnsignedByte(); }
|
||||
|
||||
@Override
|
||||
public short readShort() {
|
||||
|
||||
@@ -4,6 +4,7 @@ import java.awt.*;
|
||||
|
||||
public interface InPacket extends Packet {
|
||||
byte readByte();
|
||||
short readUnsignedByte();
|
||||
short readShort();
|
||||
int readInt();
|
||||
long readLong();
|
||||
|
||||
@@ -59,7 +59,7 @@ public final class NPCMoreTalkHandler extends AbstractPacketHandler {
|
||||
if (p.available() >= 4) {
|
||||
selection = p.readInt();
|
||||
} else if (p.available() > 0) {
|
||||
selection = p.readByte();
|
||||
selection = p.readUnsignedByte();
|
||||
}
|
||||
if (c.getQM() != null) {
|
||||
if (c.getQM().isStart()) {
|
||||
|
||||
@@ -25,8 +25,12 @@ import client.Character;
|
||||
import client.Client;
|
||||
import client.Skill;
|
||||
import client.SkillFactory;
|
||||
import client.inventory.*;
|
||||
import client.inventory.Equip;
|
||||
import client.inventory.Equip.ScrollResult;
|
||||
import client.inventory.Inventory;
|
||||
import client.inventory.InventoryType;
|
||||
import client.inventory.Item;
|
||||
import client.inventory.ModifyInventory;
|
||||
import client.inventory.manipulator.InventoryManipulator;
|
||||
import constants.id.ItemId;
|
||||
import constants.inventory.ItemConstants;
|
||||
@@ -37,7 +41,6 @@ import tools.PacketCreator;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author Matze
|
||||
@@ -50,8 +53,8 @@ public final class ScrollHandler extends AbstractPacketHandler {
|
||||
if (c.tryacquireClient()) {
|
||||
try {
|
||||
p.readInt(); // whatever...
|
||||
short slot = p.readShort();
|
||||
short dst = p.readShort();
|
||||
short scrollSlot = p.readShort();
|
||||
short equipSlot = p.readShort();
|
||||
byte ws = (byte) p.readShort();
|
||||
boolean whiteScroll = false; // white scroll being used?
|
||||
boolean legendarySpirit = false; // legendary spirit skill
|
||||
@@ -61,24 +64,21 @@ public final class ScrollHandler extends AbstractPacketHandler {
|
||||
|
||||
ItemInformationProvider ii = ItemInformationProvider.getInstance();
|
||||
Character chr = c.getPlayer();
|
||||
Equip toScroll = (Equip) chr.getInventory(InventoryType.EQUIPPED).getItem(dst);
|
||||
Equip toScroll = (Equip) chr.getInventory(InventoryType.EQUIPPED).getItem(equipSlot);
|
||||
Skill LegendarySpirit = SkillFactory.getSkill(1003);
|
||||
if (chr.getSkillLevel(LegendarySpirit) > 0 && dst >= 0) {
|
||||
if (chr.getSkillLevel(LegendarySpirit) > 0 && equipSlot >= 0) {
|
||||
legendarySpirit = true;
|
||||
toScroll = (Equip) chr.getInventory(InventoryType.EQUIP).getItem(dst);
|
||||
toScroll = (Equip) chr.getInventory(InventoryType.EQUIP).getItem(equipSlot);
|
||||
}
|
||||
byte oldLevel = toScroll.getLevel();
|
||||
byte oldSlots = toScroll.getUpgradeSlots();
|
||||
Inventory useInventory = chr.getInventory(InventoryType.USE);
|
||||
Item scroll = useInventory.getItem(slot);
|
||||
Item scroll = useInventory.getItem(scrollSlot);
|
||||
Item wscroll = null;
|
||||
|
||||
if (ItemConstants.isCleanSlate(scroll.getItemId())) {
|
||||
Map<String, Integer> eqStats = ii.getEquipStats(toScroll.getItemId()); // clean slate issue found thanks to Masterrulax
|
||||
if (eqStats == null || eqStats.get("tuc") == 0) {
|
||||
announceCannotScroll(c, legendarySpirit);
|
||||
return;
|
||||
}
|
||||
if (ItemConstants.isCleanSlate(scroll.getItemId()) && !ii.canUseCleanSlate(toScroll)) {
|
||||
announceCannotScroll(c, legendarySpirit);
|
||||
return;
|
||||
} else if (!ItemConstants.isModifierScroll(scroll.getItemId()) && toScroll.getUpgradeSlots() < 1) {
|
||||
announceCannotScroll(c, legendarySpirit); // thanks onechord for noticing zero upgrade slots freezing Legendary Scroll UI
|
||||
return;
|
||||
@@ -103,11 +103,6 @@ public final class ScrollHandler extends AbstractPacketHandler {
|
||||
}
|
||||
}
|
||||
|
||||
if (ItemConstants.isCleanSlate(scroll.getItemId()) && !ii.canUseCleanSlate(toScroll)) {
|
||||
announceCannotScroll(c, legendarySpirit);
|
||||
return;
|
||||
}
|
||||
|
||||
Equip scrolled = (Equip) ii.scrollEquipWithId(toScroll, scroll.getItemId(), whiteScroll, 0, chr.isGM());
|
||||
ScrollResult scrollSuccess = Equip.ScrollResult.FAIL; // fail
|
||||
if (scrolled == null) {
|
||||
@@ -141,7 +136,7 @@ public final class ScrollHandler extends AbstractPacketHandler {
|
||||
if (scrollSuccess == Equip.ScrollResult.CURSE) {
|
||||
if (!ItemId.isWeddingRing(toScroll.getItemId())) {
|
||||
mods.add(new ModifyInventory(3, toScroll));
|
||||
if (dst < 0) {
|
||||
if (equipSlot < 0) {
|
||||
Inventory inv = chr.getInventory(InventoryType.EQUIPPED);
|
||||
|
||||
inv.lockInventory();
|
||||
@@ -174,7 +169,7 @@ public final class ScrollHandler extends AbstractPacketHandler {
|
||||
}
|
||||
c.sendPacket(PacketCreator.modifyInventory(true, mods));
|
||||
chr.getMap().broadcastMessage(PacketCreator.getScrollEffect(chr.getId(), scrollSuccess, legendarySpirit, whiteScroll));
|
||||
if (dst < 0 && (scrollSuccess == Equip.ScrollResult.SUCCESS || scrollSuccess == Equip.ScrollResult.CURSE)) {
|
||||
if (equipSlot < 0 && (scrollSuccess == Equip.ScrollResult.SUCCESS || scrollSuccess == Equip.ScrollResult.CURSE)) {
|
||||
chr.equipChanged();
|
||||
}
|
||||
} finally {
|
||||
|
||||
@@ -228,10 +228,10 @@ public final class UseCashItemHandler extends AbstractPacketHandler {
|
||||
return;
|
||||
}
|
||||
short flag = eq.getFlag();
|
||||
flag |= ItemConstants.LOCK;
|
||||
if (eq.getExpiration() > -1) {
|
||||
if (eq.getExpiration() > -1 && (eq.getFlag() & ItemConstants.LOCK) != ItemConstants.LOCK) {
|
||||
return; //No perma items pls
|
||||
}
|
||||
flag |= ItemConstants.LOCK;
|
||||
eq.setFlag(flag);
|
||||
|
||||
long period = 0;
|
||||
@@ -246,7 +246,8 @@ public final class UseCashItemHandler extends AbstractPacketHandler {
|
||||
}
|
||||
|
||||
if (period > 0) {
|
||||
eq.setExpiration(currentServerTime() + DAYS.toMillis(period));
|
||||
long expiration = eq.getExpiration() > -1 ? eq.getExpiration() : currentServerTime();
|
||||
eq.setExpiration(expiration + DAYS.toMillis(period));
|
||||
}
|
||||
|
||||
// double-remove found thanks to BHB
|
||||
|
||||
@@ -25,43 +25,14 @@ import client.Client;
|
||||
import client.creator.novice.BeginnerCreator;
|
||||
import client.creator.novice.LegendCreator;
|
||||
import client.creator.novice.NoblesseCreator;
|
||||
import constants.id.ItemId;
|
||||
import net.AbstractPacketHandler;
|
||||
import net.packet.InPacket;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import tools.PacketCreator;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public final class CreateCharHandler extends AbstractPacketHandler {
|
||||
private static final Logger log = LoggerFactory.getLogger(CreateCharHandler.class);
|
||||
|
||||
private final static Set<Integer> IDs = new HashSet<>(Arrays.asList(
|
||||
ItemId.SWORD, ItemId.HAND_AXE, ItemId.WOODEN_CLUB, ItemId.BASIC_POLEARM,// weapons
|
||||
ItemId.WHITE_UNDERSHIRT, ItemId.UNDERSHIRT, ItemId.GREY_TSHIRT, ItemId.WHITE_TUBETOP, ItemId.YELLOW_TSHIRT,
|
||||
ItemId.GREEN_TSHIRT, ItemId.RED_STRIPED_TOP, ItemId.SIMPLE_WARRIOR_TOP,// bottom
|
||||
ItemId.BLUE_JEAN_SHORTS, ItemId.BROWN_COTTON_SHORTS, ItemId.RED_MINISKIRT, ItemId.INDIGO_MINISKIRT,
|
||||
ItemId.SIMPLE_WARRIOR_PANTS, // top
|
||||
ItemId.RED_RUBBER_BOOTS, ItemId.LEATHER_SANDALS, ItemId.YELLOW_RUBBER_BOOTS, ItemId.BLUE_RUBBER_BOOTS,
|
||||
ItemId.AVERAGE_MUSASHI_SHOES,// shoes
|
||||
ItemId.BLACK_TOBEN, ItemId.ZETA, ItemId.BLACK_REBEL, ItemId.BLACK_BUZZ, ItemId.BLACK_SAMMY,
|
||||
ItemId.BLACK_EDGY, ItemId.BLACK_CONNIE,// hair
|
||||
ItemId.MOTIVATED_LOOK_M, ItemId.PERPLEXED_STARE, ItemId.LEISURE_LOOK_M, ItemId.MOTIVATED_LOOK_F,
|
||||
ItemId.FEARFUL_STARE_M, ItemId.LEISURE_LOOK_F, ItemId.FEARFUL_STARE_F, ItemId.PERPLEXED_STARE_HAZEL,
|
||||
ItemId.LEISURE_LOOK_HAZEL, ItemId.MOTIVATED_LOOK_AMETHYST, ItemId.MOTIVATED_LOOK_BLUE //face
|
||||
//#NeverTrustStevenCode
|
||||
));
|
||||
|
||||
private static boolean isLegal(Integer toCompare) {
|
||||
return IDs.contains(toCompare);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public final void handlePacket(InPacket p, Client c) {
|
||||
public void handlePacket(InPacket p, Client c) {
|
||||
String name = p.readString();
|
||||
int job = p.readInt();
|
||||
int face = p.readInt();
|
||||
@@ -76,15 +47,6 @@ public final class CreateCharHandler extends AbstractPacketHandler {
|
||||
int weapon = p.readInt();
|
||||
int gender = p.readByte();
|
||||
|
||||
int[] items = new int[]{weapon, top, bottom, shoes, hair, face};
|
||||
for (int item : items) {
|
||||
if (!isLegal(item)) {
|
||||
log.warn("Owner from account {} tried to packet edit in chr creation", c.getAccountName());
|
||||
c.disconnect(true, false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
int status;
|
||||
switch (job) {
|
||||
case 0: // Knights of Cygnus
|
||||
|
||||
@@ -107,7 +107,7 @@ public class QuestScriptManager extends AbstractScriptManager {
|
||||
|
||||
public void end(Client c, short questid, int npc) {
|
||||
Quest quest = Quest.getInstance(questid);
|
||||
if (!c.getPlayer().getQuest(quest).getStatus().equals(QuestStatus.Status.STARTED) || !c.getPlayer().getMap().containsNPC(npc)) {
|
||||
if (!c.getPlayer().getQuest(quest).getStatus().equals(QuestStatus.Status.STARTED) || (!c.getPlayer().getMap().containsNPC(npc) && !quest.isAutoComplete())) {
|
||||
dispose(c);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -22,9 +22,16 @@
|
||||
package server;
|
||||
|
||||
import client.Character;
|
||||
import client.*;
|
||||
import client.Client;
|
||||
import client.Job;
|
||||
import client.Skill;
|
||||
import client.SkillFactory;
|
||||
import client.autoban.AutobanFactory;
|
||||
import client.inventory.*;
|
||||
import client.inventory.Equip;
|
||||
import client.inventory.Inventory;
|
||||
import client.inventory.InventoryType;
|
||||
import client.inventory.Item;
|
||||
import client.inventory.WeaponType;
|
||||
import config.YamlConfig;
|
||||
import constants.id.ItemId;
|
||||
import constants.inventory.EquipSlot;
|
||||
@@ -35,19 +42,36 @@ import constants.skills.NightWalker;
|
||||
import net.server.Server;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import provider.*;
|
||||
import provider.Data;
|
||||
import provider.DataDirectoryEntry;
|
||||
import provider.DataFileEntry;
|
||||
import provider.DataProvider;
|
||||
import provider.DataProviderFactory;
|
||||
import provider.DataTool;
|
||||
import provider.wz.WZFiles;
|
||||
import server.MakerItemFactory.MakerItemCreateEntry;
|
||||
import server.life.LifeFactory;
|
||||
import server.life.MonsterInformationProvider;
|
||||
import tools.*;
|
||||
import tools.DatabaseConnection;
|
||||
import tools.PacketCreator;
|
||||
import tools.Pair;
|
||||
import tools.Randomizer;
|
||||
import tools.StringUtil;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author Matze
|
||||
@@ -204,13 +228,13 @@ public class ItemInformationProvider {
|
||||
} else if (itemId >= 1040000 && itemId < 1050000) {
|
||||
theData = eqpStringData;
|
||||
cat = "Eqp/Coat";
|
||||
} else if (itemId >= 20000 && itemId < 22000) {
|
||||
} else if (ItemConstants.isFace(itemId)) {
|
||||
theData = eqpStringData;
|
||||
cat = "Eqp/Face";
|
||||
} else if (itemId >= 1080000 && itemId < 1090000) {
|
||||
theData = eqpStringData;
|
||||
cat = "Eqp/Glove";
|
||||
} else if (itemId >= 30000 && itemId < 35000) {
|
||||
} else if (ItemConstants.isHair(itemId)) {
|
||||
theData = eqpStringData;
|
||||
cat = "Eqp/Hair";
|
||||
} else if (itemId >= 1050000 && itemId < 1060000) {
|
||||
@@ -1025,9 +1049,16 @@ public class ItemInformationProvider {
|
||||
Issue with clean slate found thanks to Masterrulax
|
||||
Vicious added in the clean slate check thanks to Crypter (CrypterDEV)
|
||||
*/
|
||||
public boolean canUseCleanSlate(Equip nEquip) {
|
||||
Map<String, Integer> eqstats = this.getEquipStats(nEquip.getItemId());
|
||||
return YamlConfig.config.server.USE_ENHANCED_CLNSLATE || nEquip.getUpgradeSlots() < (byte) (eqstats.get("tuc") + nEquip.getVicious());
|
||||
public boolean canUseCleanSlate(Equip equip) {
|
||||
Map<String, Integer> eqStats = getEquipStats(equip.getItemId());
|
||||
if (eqStats == null || eqStats.get("tuc") == 0 ) {
|
||||
return false;
|
||||
}
|
||||
int totalUpgradeCount = eqStats.get("tuc");
|
||||
int freeUpgradeCount = equip.getUpgradeSlots();
|
||||
int viciousCount = equip.getVicious();
|
||||
int appliedScrollCount = equip.getLevel();
|
||||
return freeUpgradeCount + appliedScrollCount < totalUpgradeCount + viciousCount;
|
||||
}
|
||||
|
||||
public Item scrollEquipWithId(Item equip, int scrollId, boolean usingWhiteScroll, int vegaItemId, boolean isGM) {
|
||||
|
||||
@@ -190,6 +190,11 @@ public class MobSkill {
|
||||
|
||||
// TODO: avoid output argument banishPlayersOutput
|
||||
public void applyEffect(Character player, Monster monster, boolean skill, List<Character> banishPlayersOutput) {
|
||||
// See if the MobSkill is successful before doing anything
|
||||
if (!makeChanceResult()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Disease disease = null;
|
||||
Map<MonsterStatus, Integer> stats = new EnumMap<>(MonsterStatus.class);
|
||||
List<Integer> reflection = new ArrayList<>();
|
||||
@@ -213,12 +218,12 @@ public class MobSkill {
|
||||
case REVERSE_INPUT -> disease = Disease.CONFUSE;
|
||||
case UNDEAD -> disease = Disease.ZOMBIFY;
|
||||
case PHYSICAL_IMMUNE -> {
|
||||
if (makeChanceResult() && !monster.isBuffed(MonsterStatus.MAGIC_IMMUNITY)) {
|
||||
if (!monster.isBuffed(MonsterStatus.MAGIC_IMMUNITY)) {
|
||||
stats.put(MonsterStatus.WEAPON_IMMUNITY, x);
|
||||
}
|
||||
}
|
||||
case MAGIC_IMMUNE -> {
|
||||
if (makeChanceResult() && !monster.isBuffed(MonsterStatus.WEAPON_IMMUNITY)) {
|
||||
if (!monster.isBuffed(MonsterStatus.WEAPON_IMMUNITY)) {
|
||||
stats.put(MonsterStatus.MAGIC_IMMUNITY, x);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,6 +38,36 @@ class ByteBufInPacketTest {
|
||||
assertEquals(writtenByte, readByte);
|
||||
}
|
||||
|
||||
@Test
|
||||
void readUnsignedByte() {
|
||||
final byte writtenByte = Byte.MAX_VALUE;
|
||||
byteBuf.writeByte(writtenByte);
|
||||
|
||||
short readUnsignedByte = inPacket.readUnsignedByte();
|
||||
|
||||
assertEquals(writtenByte, readUnsignedByte);
|
||||
}
|
||||
|
||||
@Test
|
||||
void readUnsignedByte_shouldBeNonnegative() {
|
||||
final byte writtenByte = Byte.MIN_VALUE;
|
||||
byteBuf.writeByte(writtenByte);
|
||||
|
||||
short readUnsignedByte = inPacket.readUnsignedByte();
|
||||
|
||||
assertEquals((short)writtenByte + 256, readUnsignedByte);
|
||||
}
|
||||
|
||||
@Test
|
||||
void readUnsignedByte_shouldBeNonnegative2() {
|
||||
final byte writtenByte = -1;
|
||||
byteBuf.writeByte(writtenByte);
|
||||
|
||||
short readUnsignedByte = inPacket.readUnsignedByte();
|
||||
|
||||
assertEquals((short)writtenByte + 256, readUnsignedByte);
|
||||
}
|
||||
|
||||
@Test
|
||||
void readShort() {
|
||||
final short writtenShort = 12_345;
|
||||
|
||||
Reference in New Issue
Block a user