Gaikotsu 620 Опубликовано 13 сентября, 2013 Думаю все в курсе, что в Lindvior ввели Beauty Shop (салон красоты), предназначенный для изменения внешнего вида персонажей (прически, цвета волос, лица). Ну и собственно в данном руководстве я хочу более-менее подробно описать, как реализовать данное дело в сервере, в частности на сервере основанном на оверах. Итак, поехали: Первым делом создадим 3 новых поля в таблице characters, аналоги полей face, hairStyle и hairColor - назовем их к примеру newFace, newHairStyle и newHairColor. Объяснять как читать и писать в эти поля, а так же как добавить новые переменные и геттеры/сеттеры для них в класс Player я не буду - это самые основы знания Java, не знать такое просто грешно. Может сразу возникнуть вполне закономерный вопрос - а почему бы не использовать существующие поля для хранения информации о внешности? Ответ прост - в некоторых ситуациях нам потребуется знать и то, каким была внешность до посещения салона красоты, к примеру для функции отмены изменений в том же самом салоне, или же для фестиваля хаоса - новые прически на фестивале не отображаются (правда насчет этого информация неподтвержденная, но так написано в патчноутах). Далее озаботимся хранилищем данных о допустимых прическах и т.д., а так же загрузкой этих данных при запуске сервера. Хранить данные мы будем в xml-файле следующей структуры: <?xml version="1.0" encoding="utf-8"?> <!ELEMENT list (config|set)*> <!ELEMENT config (#PCDATA)*> <!ATTLIST config coin_item_id CDATA #REQUIRED def_reset_price CDATA #REQUIRED> <!ELEMENT set (hair|face)*> <!ATTLIST set id CDATA #REQUIRED> <!ELEMENT hair (color)*> <!ATTLIST hair id CDATA #REQUIRED adena CDATA #REQUIRED coins CDATA #REQUIRED reset_price CDATA #IMPLIED> <!ELEMENT color (#PCDATA)> <!ATTLIST color id CDATA #REQUIRED adena CDATA #REQUIRED coins CDATA #REQUIRED> <!ELEMENT face (#PCDATA)> <!ATTLIST face id CDATA #REQUIRED adena CDATA #REQUIRED coins CDATA #REQUIRED reset_price CDATA #IMPLIED> И для примера фрагмент xml с данными <?xml version='1.0' encoding='utf-8'?> <!DOCTYPE list SYSTEM "beauty_shop.dtd"> <list> <config coin_item_id="36308" def_reset_price="1000000" /> <!-- Human Male Fighter --> <set id="0"> <!-- Прически --> <hair id="10001" adena="15000000" coins="0" /> <!-- Настоящий мачо A --> <hair id="10002" adena="15000000" coins="0" /> <!-- Прекрасный юноша A --> <hair id="10003" adena="0" coins="4"> <!-- Пират --> <color id="101" adena="3000000" coins="0" /> <!-- Рекомендация дизайнера --> <color id="102" adena="0" coins="1" /> <!-- Светло-кобальтовое небо --> <color id="103" adena="0" coins="1" /> <!-- Лиловый виноград --> <color id="104" adena="0" coins="1" /> <!-- Розовый карандаш --> <color id="105" adena="0" coins="1" /> <!-- Черная бездна --> <color id="106" adena="0" coins="1" /> <!-- Изумрудное украшение --> <color id="107" adena="0" coins="1" /> <!-- Желтая бабочка --> <color id="108" adena="0" coins="1" /> <!-- Вечнозеленый --> <color id="109" adena="0" coins="1" /> <!-- Волшебная метель --> <color id="110" adena="0" coins="1" /> <!-- Молчание серого ветра --> </hair> <hair id="10004" adena="0" coins="4"> <!-- Настоящий смельчак --> <color id="101" adena="3000000" coins="0" /> <!-- Рекомендация дизайнера --> <color id="102" adena="0" coins="1" /> <!-- Светло-кобальтовое небо --> <color id="103" adena="0" coins="1" /> <!-- Лиловый виноград --> <color id="104" adena="0" coins="1" /> <!-- Розовый карандаш --> <color id="105" adena="0" coins="1" /> <!-- Черная бездна --> <color id="106" adena="0" coins="1" /> <!-- Изумрудное украшение --> <color id="107" adena="0" coins="1" /> <!-- Желтая бабочка --> <color id="108" adena="0" coins="1" /> <!-- Вечнозеленый --> <color id="109" adena="0" coins="1" /> <!-- Волшебная метель --> <color id="110" adena="0" coins="1" /> <!-- Молчание серого ветра --> </hair> <hair id="10005" adena="15000000" coins="0" /> <!-- Потому что она - моя женщина A --> <hair id="10006" adena="15000000" coins="0" /> <!-- Честь джентльмена A --> <hair id="10007" adena="0" coins="3" /> <!-- Настоящий мачо B --> <hair id="10008" adena="0" coins="3" /> <!-- Прекрасный юноша B --> <hair id="10009" adena="0" coins="3" /> <!-- Потому что она - моя женщина B --> <hair id="10010" adena="0" coins="3" /> <!-- Честь джентльмена B --> <!-- Лица --> <face id="20001" adena="2000000" coins="0" reset_price="500000" /> <!-- Стиль A --> <face id="20002" adena="2000000" coins="0" reset_price="500000" /> <!-- Стиль B --> <face id="20003" adena="2000000" coins="0" reset_price="500000" /> <!-- Стиль C --> <face id="20004" adena="0" coins="2" /> <!-- Стиль Орена --> <face id="20005" adena="0" coins="2" /> <!-- Стиль Годдарда --> </set> <!-- Human Female Fighter --> <set id="1"> <!-- Прически --> <hair id="10001" adena="15000000" coins="0" /> <!-- Холодная городская девушка A --> <hair id="10002" adena="15000000" coins="0" /> <!-- Грибочек A --> <hair id="10003" adena="0" coins="4"> <!-- Пират --> <color id="101" adena="3000000" coins="0" /> <!-- Рекомендация дизайнера --> <color id="102" adena="0" coins="1" /> <!-- Светло-кобальтовое небо --> <color id="103" adena="0" coins="1" /> <!-- Лиловый виноград --> <color id="104" adena="0" coins="1" /> <!-- Розовый карандаш --> <color id="105" adena="0" coins="1" /> <!-- Черная бездна --> <color id="106" adena="0" coins="1" /> <!-- Изумрудное украшение --> <color id="107" adena="0" coins="1" /> <!-- Желтая бабочка --> <color id="108" adena="0" coins="1" /> <!-- Вечнозеленый --> <color id="109" adena="0" coins="1" /> <!-- Волшебная метель --> <color id="110" adena="0" coins="1" /> <!-- Молчание серого ветра --> </hair> <hair id="10004" adena="0" coins="4"> <!-- Конский хвост --> <color id="101" adena="3000000" coins="0" /> <!-- Рекомендация дизайнера --> <color id="102" adena="0" coins="1" /> <!-- Светло-кобальтовое небо --> <color id="103" adena="0" coins="1" /> <!-- Лиловый виноград --> <color id="104" adena="0" coins="1" /> <!-- Розовый карандаш --> <color id="105" adena="0" coins="1" /> <!-- Черная бездна --> <color id="106" adena="0" coins="1" /> <!-- Изумрудное украшение --> <color id="107" adena="0" coins="1" /> <!-- Желтая бабочка --> <color id="108" adena="0" coins="1" /> <!-- Вечнозеленый --> <color id="109" adena="0" coins="1" /> <!-- Волшебная метель --> <color id="110" adena="0" coins="1" /> <!-- Молчание серого ветра --> </hair> <hair id="10005" adena="15000000" coins="0" /> <!-- Харизматичная амазонка A --> <hair id="10006" adena="15000000" coins="0" /> <!-- Унесенная ветром A --> <hair id="10007" adena="0" coins="3" /> <!-- Холодная городская девушка B --> <hair id="10008" adena="0" coins="3" /> <!-- Грибочек B --> <hair id="10009" adena="0" coins="3" /> <!-- Харизматичная амазонка B --> <hair id="10010" adena="0" coins="3" /> <!-- Унесенная ветром B --> <!-- Лица --> <face id="20001" adena="2000000" coins="0" reset_price="500000" /> <!-- Стиль A --> <face id="20002" adena="2000000" coins="0" reset_price="500000" /> <!-- Стиль B --> <face id="20003" adena="2000000" coins="0" reset_price="500000" /> <!-- Стиль C --> <face id="20004" adena="0" coins="2" /> <!-- Стиль Орена --> <face id="20005" adena="0" coins="2" /> <!-- Стиль Годдарда --> </set> ... </list> Внимательные могут сразу же заметить избыточность данных в файле - для каждой расы/пола в общем-то данные одни и те же. Я это знаю, просто обычно предпочитаю реализовывать многое с учетом того, что может быть сейчас и все одинаково для любой расы/пола, но кто их корейцев знает - могут и изменить в будущем эту ситуацию. Так что в принципе если вам такая избыточность не нужна - это легко все дорабатывается до одного единственного набора данных для любой расы/пола. Опять же это делать вам самим, тем более что это легко - ломать не строить. Далее добавляем в ядро сервера парсер этих данных и хранилище для них. Парсер: package l2p.gameserver.data.xml.parser; import l2p.commons.data.xml.AbstractFileParser; import l2p.gameserver.Config; import l2p.gameserver.data.xml.holder.BeautyShopHolder; import l2p.gameserver.model.beautyshop.BeautyShopFace; import l2p.gameserver.model.beautyshop.BeautyShopSet; import l2p.gameserver.model.beautyshop.BeautyShopHairColor; import l2p.gameserver.model.beautyshop.BeautyShopHairStyle; import l2p.gameserver.utils.Util; import org.dom4j.Element; import gnu.trove.map.hash.TIntObjectHashMap; import java.io.File; import java.util.Iterator; public final class BeautyShopParser extends AbstractFileParser<BeautyShopHolder> { private static final BeautyShopParser _instance = new BeautyShopParser(); public static BeautyShopParser getInstance() { return _instance; } private BeautyShopParser() { super(BeautyShopHolder.getInstance()); } @Override public File getXMLFile() { return new File(Config.DATAPACK_ROOT, "data/beauty_shop.xml"); } @Override public String getDTDFileName() { return "beauty_shop.dtd"; } @Override protected void readData(Element rootElement) throws Exception { long defResetPrice = 0L; for (Iterator iterator = rootElement.elementIterator("config"); iterator.hasNext(); ) { Element element = (Element) iterator.next(); Config.BS_COIN_ITEM_ID = Util.parseValue(element.attributeValue("coin_item_id"), 36308); defResetPrice = Util.parseValue(element.attributeValue("def_reset_price"), 1000000L); } for (Iterator iterator = rootElement.elementIterator("set"); iterator.hasNext(); ) { Element element = (Element) iterator.next(); int setId = Util.parseValue(element.attributeValue("id"), 0); TIntObjectHashMap<BeautyShopHairStyle> hairStyles = new TIntObjectHashMap<BeautyShopHairStyle>(); TIntObjectHashMap<BeautyShopFace> faces = new TIntObjectHashMap<BeautyShopFace>(); for (Iterator<Element> subIterator = element.elementIterator("hair"); subIterator.hasNext(); ) { Element subElement = subIterator.next(); int id = Util.parseValue(subElement.attributeValue("id"), 0); long adena = Util.parseValue(subElement.attributeValue("adena"), 0L); long coins = Util.parseValue(subElement.attributeValue("coins"), 0L); long resetPrice = Util.parseValue(subElement.attributeValue("reset_price"), defResetPrice); TIntObjectHashMap<BeautyShopHairColor> colors = new TIntObjectHashMap<BeautyShopHairColor>(); for (Iterator<Element> colorIterator = subElement.elementIterator("color"); colorIterator.hasNext(); ) { Element colorElement = colorIterator.next(); int cId = Util.parseValue(colorElement.attributeValue("id"), 0); long cAdena = Util.parseValue(colorElement.attributeValue("adena"), 0L); long cCoins = Util.parseValue(colorElement.attributeValue("coins"), 0L); colors.put(cId, new BeautyShopHairColor(cId, cAdena, cCoins)); } if (colors.isEmpty()) colors.put(101, new BeautyShopHairColor(101, 0, 0)); hairStyles.put(id, new BeautyShopHairStyle(id, adena, coins, resetPrice, colors)); } for (Iterator<Element> subIterator = element.elementIterator("face"); subIterator.hasNext(); ) { Element subElement = subIterator.next(); int id = Util.parseValue(subElement.attributeValue("id"), 0); long adena = Util.parseValue(subElement.attributeValue("adena"), 0L); long coins = Util.parseValue(subElement.attributeValue("coins"), 0L); long resetPrice = Util.parseValue(subElement.attributeValue("reset_price"), defResetPrice); faces.put(id, new BeautyShopFace(id, adena, coins, resetPrice)); } getHolder().addSet(new BeautyShopSet(setId, hairStyles, faces)); } } } Хранилище: package l2p.gameserver.data.xml.holder; import gnu.trove.map.hash.TIntObjectHashMap; import l2p.commons.data.xml.AbstractHolder; import l2p.gameserver.model.Player; import l2p.gameserver.model.base.Race; import l2p.gameserver.model.base.Sex; import l2p.gameserver.model.beautyshop.BeautyShopSet; public final class BeautyShopHolder extends AbstractHolder { private static final BeautyShopHolder _instance = new BeautyShopHolder(); private static final TIntObjectHashMap<BeautyShopSet> _sets = new TIntObjectHashMap<BeautyShopSet>(); public static BeautyShopHolder getInstance() { return _instance; } public void addSet(BeautyShopSet set) { if (_sets.containsKey(set.getId())) warn("Duplicate set declaration, set id - " + set.getId()); _sets.put(set.getId(), set); } public BeautyShopSet getSet(int id) { return _sets.get(id); } public BeautyShopSet getSet(Player player) { int id = -1; if (player.getRace() == Race.HUMAN) { if (!player.getClassId().isMage()) id = player.getSex() == Sex.MALE ? 0 : 1; else id = player.getSex() == Sex.MALE ? 8 : 9; } else if (player.getRace() == Race.DARK_ELF) id = player.getSex() == Sex.MALE ? 2 : 3; else if (player.getRace() == Race.DWARF) id = player.getSex() == Sex.MALE ? 4 : 5; else if (player.getRace() == Race.ELF) id = player.getSex() == Sex.MALE ? 6 : 7; else if (player.getRace() == Race.ORC) { if (!player.getClassId().isMage()) id = player.getSex() == Sex.MALE ? 10 : 11; else id = player.getSex() == Sex.MALE ? 12 : 13; } else if (player.getRace() == Race.KAMAEL) id = player.getSex() == Sex.MALE ? 14 : 15; return getSet(id); } @Override public int size() { return _sets.size(); } @Override public void clear() { _sets.clear(); } @Override public void log() { info(String.format("loaded %d beauty shop set(s) count.", size())); } } Структуры для хранения данных: package l2p.gameserver.model.beautyshop; import gnu.trove.map.hash.TIntObjectHashMap; public class BeautyShopSet { private final int _id; private final TIntObjectHashMap<BeautyShopHairStyle> _hairStyles; private final TIntObjectHashMap<BeautyShopFace> _faces; public BeautyShopSet(int id, TIntObjectHashMap<BeautyShopHairStyle> hairStyles, TIntObjectHashMap<BeautyShopFace> faces) { _id = id; _hairStyles = hairStyles; _faces = faces; } public int getId() { return _id; } public TIntObjectHashMap<BeautyShopHairStyle> getHairStyles() { return _hairStyles; } public BeautyShopHairStyle getHairStyle(int id) { return _hairStyles.get(id); } public TIntObjectHashMap<BeautyShopFace> getFaces() { return _faces; } public BeautyShopFace getFace(int id) { return _faces.get(id); } } package l2p.gameserver.model.beautyshop; public class BeautyShopFace { private final int _id; private final long _adena; private final long _coins; private final long _resetPrice; public BeautyShopFace(int id, long adena, long coins, long resetPrice) { _id = id; _adena = adena; _coins = coins; _resetPrice = resetPrice; } public int getId() { return _id; } public long getAdena() { return _adena; } public long getCoins() { return _coins; } public long getResetPrice() { return _resetPrice; } } package l2p.gameserver.model.beautyshop; import gnu.trove.map.hash.TIntObjectHashMap; public class BeautyShopHairStyle { private final int _id; private final long _adena; private final long _coins; private final long _resetPrice; private final TIntObjectHashMap<BeautyShopHairColor> _hairColors; public BeautyShopHairStyle(int id, long adena, long coins, long resetPrice, TIntObjectHashMap<BeautyShopHairColor> hairColors) { _id = id; _adena = adena; _coins = coins; _resetPrice = resetPrice; _hairColors = hairColors; } public int getId() { return _id; } public long getAdena() { return _adena; } public long getCoins() { return _coins; } public long getResetPrice() { return _resetPrice; } public TIntObjectHashMap<BeautyShopHairColor> getHairColors() { return _hairColors; } public BeautyShopHairColor getHairColor(int id) { return _hairColors.get(id); } } package l2p.gameserver.model.beautyshop; public class BeautyShopHairColor { private final int _id; private final long _adena; private final long _coins; public BeautyShopHairColor(int id, long adena, long coins) { _id = id; _adena = adena; _coins = coins; } public int getId() { return _id; } public long getAdena() { return _adena; } public long getCoins() { return _coins; } } В парсере вы можете заметить для чтения значений используется некие методы c названием Util.parseValue( ... ) - приводить их код я не буду, т.к. и так понятно как и что они делают - можете просто написать свою реализацию их или же использовать уже что-то готовое (методы, выполняющие примерно те же функции, насколько помню вроде как были и в оригинальном овере). Далее нам необходимо внести мелкие изменения в пакеты UserInfo, CharInfo и GMViewCharacterInfo, для корректного отображения новой внешности. Изменения простейшие, типа таких: _hairStyle = player.getNewHairStyle() > 0 ? player.getNewHairStyle() : player.getHairStyle(); _hairColor = player.getNewHairColor() > 0 ? player.getNewHairColor() : player.getHairColor(); _face = player.getNewFace() > 0 ? player.getNewFace() : player.getFace(); Ну и напоследок самое главное - все пакеты, связанные с салоном красоты. Объяснять их содержимое думаю нет смысла - они достаточно просты и по их коду и так все понятно. Но сначала не забудем добавить в GamePacketHandler обработку необходимых клиентских пакетов: case 0xD7: msg = new RequestShowBeautyList(); break; case 0xD8: msg = new RequestRegistBeauty(); break; case 0xDA: msg = new RequestShowResetShopList(); break; case 0xEE: msg = new NotifyExitBeautyShop(); break; Ну и непосредственно сами пакеты. Клиентские: package l2p.gameserver.network.clientpackets; import l2p.gameserver.model.Player; import l2p.gameserver.network.serverpackets.ExResponseBeautyList; public class RequestShowBeautyList extends L2GameClientPacket { private int _type; @Override protected void readImpl() { _type = readD(); } @Override protected void runImpl() { Player player = getClient().getActiveChar(); if (player == null) return; player.sendPacket(new ExResponseBeautyList(player, _type)); } } package l2p.gameserver.network.clientpackets; import l2p.gameserver.Config; import l2p.gameserver.data.xml.holder.BeautyShopHolder; import l2p.gameserver.model.Player; import l2p.gameserver.model.beautyshop.BeautyShopSet; import l2p.gameserver.network.serverpackets.ExResponseBeautyList; import l2p.gameserver.network.serverpackets.ExResponseBeautyRegistReset; public class RequestRegistBeauty extends L2GameClientPacket { private int _hairStyle, _face, _hairColor; @Override protected void readImpl() { _hairStyle = readD(); _face = readD(); _hairColor = readD(); } @Override protected void runImpl() { Player player = getClient().getActiveChar(); if (player == null) return; long reqAdena = 0; long reqCoins = 0; boolean change = false; BeautyShopSet set = BeautyShopHolder.getInstance().getSet(player); if (set == null) { player.sendPacket(new ExResponseBeautyRegistReset(player, 0, 0), new ExResponseBeautyList(player, 0)); return; } if (_hairStyle > 0 && _hairColor > 0 && (_hairStyle != player.getNewHairStyle() || _hairColor != player.getNewHairColor())) { if (set.getHairStyle(_hairStyle) == null || set.getHairStyle(_hairStyle).getHairColor(_hairColor) == null) { player.sendPacket(new ExResponseBeautyRegistReset(player, 0, 0), new ExResponseBeautyList(player, 0)); return; } if (_hairStyle != player.getNewHairStyle()) { reqAdena += set.getHairStyle(_hairStyle).getAdena() + set.getHairStyle(_hairStyle).getHairColor(_hairColor).getAdena(); reqCoins += set.getHairStyle(_hairStyle).getCoins() + set.getHairStyle(_hairStyle).getHairColor(_hairColor).getCoins(); } else { reqAdena += set.getHairStyle(_hairStyle).getHairColor(_hairColor).getAdena(); reqCoins += set.getHairStyle(_hairStyle).getHairColor(_hairColor).getCoins(); } change = true; } if (_face > 0 && _face != player.getNewFace()) { if (set.getFace(_face) == null) { player.sendPacket(new ExResponseBeautyRegistReset(player, 0, 0), new ExResponseBeautyList(player, 0)); return; } reqAdena += set.getFace(_face).getAdena(); reqCoins += set.getFace(_face).getCoins(); change = true; } if (!change || player.getItemCount(57) < reqAdena || player.getItemCount(Config.BS_COIN_ITEM_ID) < reqCoins) { player.sendPacket(new ExResponseBeautyRegistReset(player, 0, 0), new ExResponseBeautyList(player, 0)); return; } if (reqAdena > 0) player.getInventory().destroyItemByItemId(57, reqAdena); if (reqCoins > 0) player.getInventory().destroyItemByItemId(Config.BS_COIN_ITEM_ID, reqCoins); if (_hairStyle > 0) { player.setNewHairStyle(_hairStyle); player.setNewHairColor(_hairColor); } if (_face > 0) player.setNewFace(_face); player.sendPacket(new ExResponseBeautyRegistReset(player, 0, 1), new ExResponseBeautyList(player, 0)); } } package l2p.gameserver.network.clientpackets; import l2p.gameserver.data.xml.holder.BeautyShopHolder; import l2p.gameserver.model.Player; import l2p.gameserver.model.beautyshop.BeautyShopSet; import l2p.gameserver.network.serverpackets.ExResponseBeautyRegistReset; public class RequestShowResetShopList extends L2GameClientPacket { private int _hairStyle, _face, _hairColor; @Override protected void readImpl() { _hairStyle = readD(); _face = readD(); _hairColor = readD(); } @Override protected void runImpl() { Player player = getClient().getActiveChar(); if (player == null) return; long reqAdena = 0; boolean reset = false; BeautyShopSet set = BeautyShopHolder.getInstance().getSet(player); if (set == null) { player.sendPacket(new ExResponseBeautyRegistReset(player, 1, 0)); return; } if (_hairStyle > 0 && _hairColor > 0) { if (set.getHairStyle(_hairStyle) == null) { player.sendPacket(new ExResponseBeautyRegistReset(player, 1, 0)); return; } reqAdena += set.getHairStyle(_hairStyle).getResetPrice(); reset = true; } if (_face > 0) { if (set.getFace(_face) == null) { player.sendPacket(new ExResponseBeautyRegistReset(player, 1, 0)); return; } reqAdena += set.getFace(_face).getResetPrice(); reset = true; } if (!reset || player.getAdena() < reqAdena) { player.sendPacket(new ExResponseBeautyRegistReset(player, 1, 0)); return; } player.getInventory().destroyItemByItemId(57, reqAdena); if (_hairStyle > 0) { player.setNewHairStyle(0); player.setNewHairColor(0); } if (_face > 0) player.setNewFace(0); player.sendPacket(new ExResponseBeautyRegistReset(player, 1, 1)); } } package l2p.gameserver.network.clientpackets; import l2p.gameserver.model.Player; public class NotifyExitBeautyShop extends L2GameClientPacket { @Override protected void readImpl() {} @Override protected void runImpl() { Player player = getClient().getActiveChar(); if (player == null) return; player.broadcastCharInfo(); } } И серверные пакеты: package l2p.gameserver.network.serverpackets; import l2p.gameserver.network.serverpackets.components.ServerPacket; public class ExShowBeautyMenu extends L2GameServerPacket { public int _type; // 0 - выбор нового внешнего вида, 1 - возврат старого public ExShowBeautyMenu(int type) { _type = type; } @Override protected void writeImpl() { writeEx(ServerPacket.ExShowBeautyMenu); writeD(_type); } } package l2p.gameserver.network.serverpackets; import l2p.gameserver.Config; import l2p.gameserver.data.xml.holder.BeautyShopHolder; import l2p.gameserver.model.Player; import l2p.gameserver.model.beautyshop.BeautyShopFace; import l2p.gameserver.model.beautyshop.BeautyShopHairStyle; import l2p.gameserver.model.beautyshop.BeautyShopSet; import l2p.gameserver.network.serverpackets.components.ServerPacket; public class ExResponseBeautyList extends L2GameServerPacket { private int _type; private long _adena; private long _coins; private int[][] _data; private boolean _send = false; public ExResponseBeautyList(Player player, int type) { BeautyShopSet set = BeautyShopHolder.getInstance().getSet(player); if (set == null) return; int i = 0; if (type == 0) { _data = new int[set.getHairStyles().size()][2]; for (BeautyShopHairStyle style : set.getHairStyles().valueCollection()) { _data[i][0] = style.getId(); _data[i][1] = 0; i++; } } else { _data = new int[set.getFaces().size()][2]; for (BeautyShopFace face : set.getFaces().valueCollection()) { _data[i][0] = face.getId(); _data[i][1] = 0; i++; } } _type = type; _adena = player.getAdena(); _coins = player.getItemCount(Config.BS_COIN_ITEM_ID); _send = true; } @Override protected void writeImpl() { if (!_send) return; writeEx(ServerPacket.ExResponseBeautyList); writeQ(_adena); writeQ(_coins); writeD(_type); writeD(_data.length); for (int[] element : _data) { writeD(element[0]); writeD(element[1]); } } } package l2p.gameserver.network.serverpackets; import l2p.gameserver.Config; import l2p.gameserver.model.Player; import l2p.gameserver.network.serverpackets.components.ServerPacket; public class ExResponseBeautyRegistReset extends L2GameServerPacket { private int _type; private int _result; private int _hairStyle; private int _hairColor; private int _face; private long _adena; private long _coins; public ExResponseBeautyRegistReset(Player player, int type, int result) { _type = type; _result = result; _hairStyle = player.getNewHairStyle() > 0 ? player.getNewHairStyle() : player.getHairStyle(); _hairColor = player.getNewHairColor() > 0 ? player.getNewHairColor() : player.getHairColor(); _face = player.getNewFace() > 0 ? player.getNewFace() : player.getFace(); _adena = player.getAdena(); _coins = player.getItemCount(Config.BS_COIN_ITEM_ID); } @Override protected void writeImpl() { writeEx(ServerPacket.ExResponseBeautyRegistReset); writeQ(_adena); writeQ(_coins); writeD(_type); writeD(_result); writeD(_hairStyle); writeD(_face); writeD(_hairColor); } } package l2p.gameserver.network.serverpackets; import l2p.gameserver.Config; import l2p.gameserver.model.Player; import l2p.gameserver.network.serverpackets.components.ServerPacket; public class ExResponseResetList extends L2GameServerPacket { private int _hairStyle; private int _hairColor; private int _face; private long _adena; private long _coins; public ExResponseResetList(Player player) { _hairStyle = player.getHairStyle(); _hairColor = player.getHairColor(); _face = player.getFace(); _adena = player.getAdena(); _coins = player.getItemCount(Config.BS_COIN_ITEM_ID); } @Override protected void writeImpl() { writeEx(ServerPacket.ExResponseResetList); writeQ(_adena); writeQ(_coins); writeD(_hairStyle); writeD(_face); writeD(_hairColor); } } И опкоды этих пакетов: public static final int ExShowBeautyMenu = 0x13E; public static final int ExResponseBeautyList = 0x13F; public static final int ExResponseBeautyRegistReset = 0x140; public static final int ExResponseResetList = 0x141; Ну вот собственно и все. Если я что и не описал или не пояснил - это в основном мелочи, до которых вы сами сможете без проблем додуматься. 8 Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты
localhost 715 Опубликовано 13 сентября, 2013 не шара, а мануал 1 Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты
Gaikotsu 620 Опубликовано 13 сентября, 2013 ну значит пусть в мануалы переносят модеры Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты
Vоva 23 Опубликовано 13 сентября, 2013 лайк Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты
localhost 715 Опубликовано 13 сентября, 2013 напиши в мануалах сам, это не перенесешь Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты