From ce038fd2d0bbe428c41d2ca370df5bdeb623ae7f Mon Sep 17 00:00:00 2001 From: HorridoJoho <HorridoJoho@l2jserver.com> Date: Sun, 30 Aug 2020 22:00:54 +0200 Subject: [PATCH] Buffer Service for L2J Requires CustomNpcData=True in general.properties. Requires l2j-server-game update for config/bufferservice.properties. Default npc in data/stats/npcs/custom/custom_bufferservice.xml (id 60001). Take 'Divine Inspiration' into consideration when calculating max buffs per unique bufflist. Suggested by @JMD --- .../service/base/CustomServiceScript.java | 214 +++++++ .../custom/service/base/DialogType.java | 29 + .../model/entity/CustomServiceProduct.java | 51 ++ .../model/entity/CustomServiceServer.java | 71 +++ .../service/base/model/entity/IRefable.java | 29 + .../base/model/entity/ItemRequirement.java | 72 +++ .../service/base/model/entity/Refable.java | 60 ++ .../service/base/util/CommandProcessor.java | 55 ++ .../base/util/htmltmpls/HTMLTemplateFunc.java | 88 +++ .../util/htmltmpls/HTMLTemplateParser.java | 154 +++++ .../htmltmpls/HTMLTemplatePlaceholder.java | 123 ++++ .../util/htmltmpls/HTMLTemplateUtils.java | 129 +++++ .../htmltmpls/funcs/ChildrenCountFunc.java | 49 ++ .../base/util/htmltmpls/funcs/ExistsFunc.java | 85 +++ .../util/htmltmpls/funcs/ForeachFunc.java | 102 ++++ .../util/htmltmpls/funcs/IfChildrenFunc.java | 144 +++++ .../base/util/htmltmpls/funcs/IfFunc.java | 136 +++++ .../util/htmltmpls/funcs/IncludeFunc.java | 50 ++ .../custom/service/buffer/BufferService.java | 539 ++++++++++++++++++ .../buffer/BufferServiceBypassHandler.java | 65 +++ .../buffer/BufferServiceItemHandler.java | 54 ++ .../buffer/BufferServiceRepository.java | 260 +++++++++ .../BufferServiceVoicedCommandHandler.java | 58 ++ .../service/buffer/model/BufferConfig.java | 116 ++++ .../service/buffer/model/GlobalConfig.java | 56 ++ .../service/buffer/model/UniqueBufflist.java | 87 +++ .../buffer/model/entity/AbstractBuffer.java | 97 ++++ .../buffer/model/entity/BuffCategory.java | 68 +++ .../buffer/model/entity/BuffSkill.java | 83 +++ .../buffer/model/entity/NpcBuffer.java | 64 +++ .../buffer/model/entity/VoicedBuffer.java | 49 ++ .../Q00325_GrimCollector.java | 10 +- src/main/resources/data/scripts.cfg | 1 + .../buffer/html/community/category.html | 27 + .../community/inc/active_unique_table.html | 10 + .../buffer/html/community/inc/header.html | 36 ++ .../service/buffer/html/community/main.html | 20 + .../service/buffer/html/npc/category.html | 42 ++ .../data/service/buffer/html/npc/main.html | 67 +++ .../data/service/buffer/html/npc/unique.html | 17 + .../service/buffer/json/documentation.txt | 85 +++ .../data/service/buffer/json/global.json | 197 +++++++ .../data/service/buffer/json/npcs/60001.json | 11 + .../data/service/buffer/json/voiced.json | 9 + .../npcs/custom/custom_bufferservice.xml | 9 + .../custom/custom_buffer_service_1_ulists.sql | 8 + .../custom_buffer_service_2_ulist_buffs.sql | 6 + 47 files changed, 3787 insertions(+), 5 deletions(-) create mode 100644 src/main/java/com/l2jserver/datapack/custom/service/base/CustomServiceScript.java create mode 100644 src/main/java/com/l2jserver/datapack/custom/service/base/DialogType.java create mode 100644 src/main/java/com/l2jserver/datapack/custom/service/base/model/entity/CustomServiceProduct.java create mode 100644 src/main/java/com/l2jserver/datapack/custom/service/base/model/entity/CustomServiceServer.java create mode 100644 src/main/java/com/l2jserver/datapack/custom/service/base/model/entity/IRefable.java create mode 100644 src/main/java/com/l2jserver/datapack/custom/service/base/model/entity/ItemRequirement.java create mode 100644 src/main/java/com/l2jserver/datapack/custom/service/base/model/entity/Refable.java create mode 100644 src/main/java/com/l2jserver/datapack/custom/service/base/util/CommandProcessor.java create mode 100644 src/main/java/com/l2jserver/datapack/custom/service/base/util/htmltmpls/HTMLTemplateFunc.java create mode 100644 src/main/java/com/l2jserver/datapack/custom/service/base/util/htmltmpls/HTMLTemplateParser.java create mode 100644 src/main/java/com/l2jserver/datapack/custom/service/base/util/htmltmpls/HTMLTemplatePlaceholder.java create mode 100644 src/main/java/com/l2jserver/datapack/custom/service/base/util/htmltmpls/HTMLTemplateUtils.java create mode 100644 src/main/java/com/l2jserver/datapack/custom/service/base/util/htmltmpls/funcs/ChildrenCountFunc.java create mode 100644 src/main/java/com/l2jserver/datapack/custom/service/base/util/htmltmpls/funcs/ExistsFunc.java create mode 100644 src/main/java/com/l2jserver/datapack/custom/service/base/util/htmltmpls/funcs/ForeachFunc.java create mode 100644 src/main/java/com/l2jserver/datapack/custom/service/base/util/htmltmpls/funcs/IfChildrenFunc.java create mode 100644 src/main/java/com/l2jserver/datapack/custom/service/base/util/htmltmpls/funcs/IfFunc.java create mode 100644 src/main/java/com/l2jserver/datapack/custom/service/base/util/htmltmpls/funcs/IncludeFunc.java create mode 100644 src/main/java/com/l2jserver/datapack/custom/service/buffer/BufferService.java create mode 100644 src/main/java/com/l2jserver/datapack/custom/service/buffer/BufferServiceBypassHandler.java create mode 100644 src/main/java/com/l2jserver/datapack/custom/service/buffer/BufferServiceItemHandler.java create mode 100644 src/main/java/com/l2jserver/datapack/custom/service/buffer/BufferServiceRepository.java create mode 100644 src/main/java/com/l2jserver/datapack/custom/service/buffer/BufferServiceVoicedCommandHandler.java create mode 100644 src/main/java/com/l2jserver/datapack/custom/service/buffer/model/BufferConfig.java create mode 100644 src/main/java/com/l2jserver/datapack/custom/service/buffer/model/GlobalConfig.java create mode 100644 src/main/java/com/l2jserver/datapack/custom/service/buffer/model/UniqueBufflist.java create mode 100644 src/main/java/com/l2jserver/datapack/custom/service/buffer/model/entity/AbstractBuffer.java create mode 100644 src/main/java/com/l2jserver/datapack/custom/service/buffer/model/entity/BuffCategory.java create mode 100644 src/main/java/com/l2jserver/datapack/custom/service/buffer/model/entity/BuffSkill.java create mode 100644 src/main/java/com/l2jserver/datapack/custom/service/buffer/model/entity/NpcBuffer.java create mode 100644 src/main/java/com/l2jserver/datapack/custom/service/buffer/model/entity/VoicedBuffer.java create mode 100644 src/main/resources/data/service/buffer/html/community/category.html create mode 100644 src/main/resources/data/service/buffer/html/community/inc/active_unique_table.html create mode 100644 src/main/resources/data/service/buffer/html/community/inc/header.html create mode 100644 src/main/resources/data/service/buffer/html/community/main.html create mode 100644 src/main/resources/data/service/buffer/html/npc/category.html create mode 100644 src/main/resources/data/service/buffer/html/npc/main.html create mode 100644 src/main/resources/data/service/buffer/html/npc/unique.html create mode 100644 src/main/resources/data/service/buffer/json/documentation.txt create mode 100644 src/main/resources/data/service/buffer/json/global.json create mode 100644 src/main/resources/data/service/buffer/json/npcs/60001.json create mode 100644 src/main/resources/data/service/buffer/json/voiced.json create mode 100644 src/main/resources/data/stats/npcs/custom/custom_bufferservice.xml create mode 100644 src/main/resources/sql/custom/custom_buffer_service_1_ulists.sql create mode 100644 src/main/resources/sql/custom/custom_buffer_service_2_ulist_buffs.sql diff --git a/src/main/java/com/l2jserver/datapack/custom/service/base/CustomServiceScript.java b/src/main/java/com/l2jserver/datapack/custom/service/base/CustomServiceScript.java new file mode 100644 index 0000000000..2c9d6d27ad --- /dev/null +++ b/src/main/java/com/l2jserver/datapack/custom/service/base/CustomServiceScript.java @@ -0,0 +1,214 @@ +/* + * Copyright © 2004-2020 L2J DataPack + * + * This file is part of L2J DataPack. + * + * L2J DataPack is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * L2J DataPack is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +package com.l2jserver.datapack.custom.service.base; + +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.ConcurrentHashMap; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.l2jserver.datapack.ai.npc.AbstractNpcAI; +import com.l2jserver.datapack.custom.service.base.model.entity.CustomServiceProduct; +import com.l2jserver.datapack.custom.service.base.model.entity.CustomServiceServer; +import com.l2jserver.datapack.custom.service.base.model.entity.ItemRequirement; +import com.l2jserver.datapack.custom.service.base.util.CommandProcessor; +import com.l2jserver.datapack.custom.service.base.util.htmltmpls.HTMLTemplateParser; +import com.l2jserver.datapack.custom.service.base.util.htmltmpls.HTMLTemplatePlaceholder; +import com.l2jserver.datapack.custom.service.base.util.htmltmpls.funcs.ChildrenCountFunc; +import com.l2jserver.datapack.custom.service.base.util.htmltmpls.funcs.ExistsFunc; +import com.l2jserver.datapack.custom.service.base.util.htmltmpls.funcs.ForeachFunc; +import com.l2jserver.datapack.custom.service.base.util.htmltmpls.funcs.IfChildrenFunc; +import com.l2jserver.datapack.custom.service.base.util.htmltmpls.funcs.IfFunc; +import com.l2jserver.datapack.custom.service.base.util.htmltmpls.funcs.IncludeFunc; +import com.l2jserver.gameserver.model.actor.L2Character; +import com.l2jserver.gameserver.model.actor.L2Npc; +import com.l2jserver.gameserver.model.actor.instance.L2PcInstance; +import com.l2jserver.gameserver.model.zone.ZoneId; +import com.l2jserver.gameserver.network.serverpackets.NpcHtmlMessage; +import com.l2jserver.gameserver.taskmanager.AttackStanceTaskManager; +import com.l2jserver.gameserver.util.Util; + +/** + * Custom service abstract script. + * @author HorridoJoho + * @version 2.6.2.0 + */ +public abstract class CustomServiceScript extends AbstractNpcAI { + + private static final Logger LOG = LoggerFactory.getLogger(CustomServiceScript.class); + + public static final String SCRIPT_COLLECTION = "service"; + + private final String scriptName; + + private final Path scriptPath; + + private final Map<Integer, String> lastPlayerHtmls = new ConcurrentHashMap<>(); + + public CustomServiceScript(String name) { + super(name, SCRIPT_COLLECTION); + + Objects.requireNonNull(name); + scriptName = name; + scriptPath = Paths.get(SCRIPT_COLLECTION, scriptName); + } + + private void setLastPlayerHtml(L2PcInstance player, String command) { + lastPlayerHtmls.put(player.getObjectId(), command); + } + + private void showLastPlayerHtml(L2PcInstance player, L2Npc npc) { + String lastHtmlCommand = lastPlayerHtmls.get(player.getObjectId()); + if (lastHtmlCommand != null) { + executeHtmlCommand(player, npc, new CommandProcessor(lastHtmlCommand)); + } + } + + private String generateAdvancedHtml(L2PcInstance player, CustomServiceServer service, String path, Map<String, HTMLTemplatePlaceholder> placeholders) { + final String htmlPath = "/data/" + scriptPath + "/html/" + service.getHtmlFolder() + "/" + path; + debug(player, htmlPath); + return HTMLTemplateParser.fromCache(htmlPath, player, placeholders, IncludeFunc.INSTANCE, IfFunc.INSTANCE, ForeachFunc.INSTANCE, ExistsFunc.INSTANCE, IfChildrenFunc.INSTANCE, ChildrenCountFunc.INSTANCE); + } + + protected final boolean isInsideAnyZoneOf(L2Character character, ZoneId first, ZoneId... more) { + if (character.isInsideZone(first)) { + return true; + } + + if (more != null) { + for (ZoneId zone : more) { + if (character.isInsideZone(zone)) { + return true; + } + } + } + return false; + } + + protected final void fillItemAmountMap(Map<Integer, Long> items, CustomServiceProduct product) { + for (ItemRequirement item : product.getItems()) { + Long amount = items.get(item.getItem().getId()); + if (amount == null) { + amount = 0L; + } + items.put(item.getItem().getId(), amount + item.getItemAmount()); + } + } + + protected final void showAdvancedHtml(L2PcInstance player, CustomServiceServer service, L2Npc npc, String path, Map<String, HTMLTemplatePlaceholder> placeholders) { + placeholders.put(service.getHtmlAccessorName(), service.getPlaceholder()); + String html = generateAdvancedHtml(player, service, path, placeholders); + + debug(html); + + switch (service.getDialogType()) { + case NPC: + player.sendPacket(new NpcHtmlMessage(npc == null ? 0 : npc.getObjectId(), html)); + break; + case COMMUNITY: + Util.sendCBHtml(player, html, npc == null ? 0 : npc.getObjectId()); + break; + } + } + + @Override + public final String onFirstTalk(L2Npc npc, L2PcInstance player) { + executeCommand(player, npc, null); + return null; + } + + public final void debug(L2PcInstance player, String message) { + if (player.isGM() && isDebugEnabled()) { + player.sendMessage(scriptName + ": " + message); + } + } + + public final void debug(String message) { + if (isDebugEnabled()) { + LOG.info("Custom Service Debug:" + message); + } + } + + public final void executeCommand(L2PcInstance player, L2Npc npc, String commandString) { + if (isInsideAnyZoneOf(player, ZoneId.PVP, ZoneId.SIEGE, ZoneId.WATER, ZoneId.JAIL, ZoneId.DANGER_AREA)) { + player.sendMessage("The service cannot be used here."); + return; + } else if ((player.getEventStatus() != null) || (player.getBlockCheckerArena() != -1) || player.isOnEvent() || player.isInOlympiadMode()) { + player.sendMessage("The service cannot be used in events."); + return; + } else if (player.isInDuel() || (player.getPvpFlag() == 1)) { + player.sendMessage("The service cannot be used in duel or pvp."); + return; + } else if (AttackStanceTaskManager.getInstance().hasAttackStanceTask(player)) { + player.sendMessage("The service cannot be used while in combat."); + return; + } + + if ((commandString == null) || commandString.isEmpty()) { + commandString = "html main"; + } + + debug(player, "--------------------"); + debug(player, commandString); + + CommandProcessor command = new CommandProcessor(commandString); + + if (command.matchAndRemove("html ", "h ")) { + String playerCommand = command.getRemaining(); + if (!executeHtmlCommand(player, npc, command)) { + setLastPlayerHtml(player, "main"); + } else { + setLastPlayerHtml(player, playerCommand); + } + } else { + if (executeActionCommand(player, npc, command)) { + showLastPlayerHtml(player, npc); + } + } + } + + /** + * Method for Html command processing. The default html command is "main".<br> + * This also means, "main" must be implemented. The return value indicates if the user supplied html command should be saved as last html command. + * @param player + * @param npc + * @param command + * @return {@code true} save the html command as last html command, {@code false} don't save the html command as last html command + */ + protected abstract boolean executeHtmlCommand(L2PcInstance player, L2Npc npc, CommandProcessor command); + + /** + * Method for action command processing. The return value indicates if the last saved player html command should be executed after this method. + * @param player + * @param npc + * @param command + * @return {@code true} execute last saved html command of the player, {@code false} don't execute last saved html command of the player + */ + protected abstract boolean executeActionCommand(L2PcInstance player, L2Npc npc, CommandProcessor command); + + /** + * Method to determine if debugging is enabled. + * @return {@code true} debugging is enabled, {@code false} debugging is disabled + */ + protected abstract boolean isDebugEnabled(); +} diff --git a/src/main/java/com/l2jserver/datapack/custom/service/base/DialogType.java b/src/main/java/com/l2jserver/datapack/custom/service/base/DialogType.java new file mode 100644 index 0000000000..3692ffaaab --- /dev/null +++ b/src/main/java/com/l2jserver/datapack/custom/service/base/DialogType.java @@ -0,0 +1,29 @@ +/* + * Copyright © 2004-2020 L2J DataPack + * + * This file is part of L2J DataPack. + * + * L2J DataPack is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * L2J DataPack is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +package com.l2jserver.datapack.custom.service.base; + +/** + * Dialog Type enum. + * @author HorridoJoho + * @version 2.6.2.0 + */ +public enum DialogType { + NPC, + COMMUNITY +} diff --git a/src/main/java/com/l2jserver/datapack/custom/service/base/model/entity/CustomServiceProduct.java b/src/main/java/com/l2jserver/datapack/custom/service/base/model/entity/CustomServiceProduct.java new file mode 100644 index 0000000000..7cbda9caca --- /dev/null +++ b/src/main/java/com/l2jserver/datapack/custom/service/base/model/entity/CustomServiceProduct.java @@ -0,0 +1,51 @@ +/* + * Copyright © 2004-2020 L2J DataPack + * + * This file is part of L2J DataPack. + * + * L2J DataPack is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * L2J DataPack is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +package com.l2jserver.datapack.custom.service.base.model.entity; + +import java.util.List; + +import com.l2jserver.datapack.custom.service.base.util.htmltmpls.HTMLTemplatePlaceholder; + +/** + * Custom Service Product. + * @author HorridoJoho + * @version 2.6.2.0 + */ +public abstract class CustomServiceProduct extends Refable { + private List<ItemRequirement> items; + + protected CustomServiceProduct() { + } + + @Override + public void afterDeserialize() { + super.afterDeserialize(); + + if (!items.isEmpty()) { + HTMLTemplatePlaceholder itemsPlaceholder = getPlaceholder().addChild("items", null).getChild("items"); + for (ItemRequirement item : items) { + itemsPlaceholder.addAliasChild(String.valueOf(itemsPlaceholder.getChildrenSize()), item.getPlaceholder()); + } + } + } + + public final List<ItemRequirement> getItems() { + return items; + } +} diff --git a/src/main/java/com/l2jserver/datapack/custom/service/base/model/entity/CustomServiceServer.java b/src/main/java/com/l2jserver/datapack/custom/service/base/model/entity/CustomServiceServer.java new file mode 100644 index 0000000000..0585eb9c6b --- /dev/null +++ b/src/main/java/com/l2jserver/datapack/custom/service/base/model/entity/CustomServiceServer.java @@ -0,0 +1,71 @@ +/* + * Copyright © 2004-2020 L2J DataPack + * + * This file is part of L2J DataPack. + * + * L2J DataPack is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * L2J DataPack is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +package com.l2jserver.datapack.custom.service.base.model.entity; + +import com.l2jserver.datapack.custom.service.base.DialogType; +import com.l2jserver.datapack.custom.service.base.util.htmltmpls.HTMLTemplatePlaceholder; + +/** + * Custom Service Server. + * @author HorridoJoho + * @version 2.6.2.0 + */ +public abstract class CustomServiceServer { + private DialogType dialogType; + private String htmlFolder; + + private final transient HTMLTemplatePlaceholder placeholder; + private final transient String bypassPrefix; + private final transient String htmlAccessorName; + + public CustomServiceServer(String bypassPrefix, String htmlAccessorName) { + dialogType = DialogType.NPC; + htmlFolder = null; + + placeholder = new HTMLTemplatePlaceholder("service", null); + this.bypassPrefix = "bypass -h " + bypassPrefix; + this.htmlAccessorName = htmlAccessorName; + } + + public void afterDeserialize() { + placeholder.addChild("bypass_prefix", bypassPrefix).addChild("name", getName()); + } + + public final DialogType getDialogType() { + return dialogType; + } + + public final String getHtmlFolder() { + return htmlFolder; + } + + public final HTMLTemplatePlaceholder getPlaceholder() { + return placeholder; + } + + public final String getBypassPrefix() { + return bypassPrefix; + } + + public final String getHtmlAccessorName() { + return htmlAccessorName; + } + + public abstract String getName(); +} diff --git a/src/main/java/com/l2jserver/datapack/custom/service/base/model/entity/IRefable.java b/src/main/java/com/l2jserver/datapack/custom/service/base/model/entity/IRefable.java new file mode 100644 index 0000000000..d7bf47e79b --- /dev/null +++ b/src/main/java/com/l2jserver/datapack/custom/service/base/model/entity/IRefable.java @@ -0,0 +1,29 @@ +/* + * Copyright © 2004-2020 L2J DataPack + * + * This file is part of L2J DataPack. + * + * L2J DataPack is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * L2J DataPack is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +package com.l2jserver.datapack.custom.service.base.model.entity; + +/** + * Refable interface. + * @author HorridoJoho + * @param <T> id type + * @version 2.6.2.0 + */ +public interface IRefable<T> { + T getId(); +} diff --git a/src/main/java/com/l2jserver/datapack/custom/service/base/model/entity/ItemRequirement.java b/src/main/java/com/l2jserver/datapack/custom/service/base/model/entity/ItemRequirement.java new file mode 100644 index 0000000000..f77907ec84 --- /dev/null +++ b/src/main/java/com/l2jserver/datapack/custom/service/base/model/entity/ItemRequirement.java @@ -0,0 +1,72 @@ +/* + * Copyright © 2004-2020 L2J DataPack + * + * This file is part of L2J DataPack. + * + * L2J DataPack is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * L2J DataPack is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +package com.l2jserver.datapack.custom.service.base.model.entity; + +import com.l2jserver.datapack.custom.service.base.util.htmltmpls.HTMLTemplatePlaceholder; +import com.l2jserver.gameserver.datatables.ItemTable; +import com.l2jserver.gameserver.model.items.L2Item; + +/** + * Item Requirement. + * @author HorridoJoho + * @version 2.6.2.0 + */ +public class ItemRequirement { + private int id; + private long amount; + + private final transient HTMLTemplatePlaceholder placeholder; + + public ItemRequirement() { + id = 0; + amount = 0; + + placeholder = new HTMLTemplatePlaceholder("placeholder", null); + } + + public ItemRequirement(int id, long amount) { + this.id = id; + this.amount = amount; + + placeholder = new HTMLTemplatePlaceholder("placeholder", null); + + afterDeserialize(); + } + + public void afterDeserialize() { + final L2Item item = getItem(); + placeholder.addChild("id", String.valueOf(item.getId())).addChild("icon", item.getIcon()).addChild("name", item.getName()).addChild("amount", String.valueOf(amount)); + } + + public final int getItemId() { + return id; + } + + public final long getItemAmount() { + return amount; + } + + public HTMLTemplatePlaceholder getPlaceholder() { + return placeholder; + } + + public final L2Item getItem() { + return ItemTable.getInstance().getTemplate(id); + } +} diff --git a/src/main/java/com/l2jserver/datapack/custom/service/base/model/entity/Refable.java b/src/main/java/com/l2jserver/datapack/custom/service/base/model/entity/Refable.java new file mode 100644 index 0000000000..0bf85cc1f1 --- /dev/null +++ b/src/main/java/com/l2jserver/datapack/custom/service/base/model/entity/Refable.java @@ -0,0 +1,60 @@ +/* + * Copyright © 2004-2020 L2J DataPack + * + * This file is part of L2J DataPack. + * + * L2J DataPack is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * L2J DataPack is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +package com.l2jserver.datapack.custom.service.base.model.entity; + +import java.util.Objects; + +import com.l2jserver.datapack.custom.service.base.util.htmltmpls.HTMLTemplatePlaceholder; + +/** + * Refable. + * @author HorridoJoho + * @version 2.6.2.0 + */ +public abstract class Refable implements IRefable<String> { + private String id; + + private final transient HTMLTemplatePlaceholder placeholder; + + protected Refable() { + id = null; + + placeholder = new HTMLTemplatePlaceholder("placeholder", null); + } + + protected Refable(String id) { + Objects.requireNonNull(id); + this.id = id; + + placeholder = new HTMLTemplatePlaceholder("placeholder", null); + } + + public void afterDeserialize() { + placeholder.addChild("ident", id); + } + + @Override + public final String getId() { + return id; + } + + public final HTMLTemplatePlaceholder getPlaceholder() { + return placeholder; + } +} diff --git a/src/main/java/com/l2jserver/datapack/custom/service/base/util/CommandProcessor.java b/src/main/java/com/l2jserver/datapack/custom/service/base/util/CommandProcessor.java new file mode 100644 index 0000000000..2212301132 --- /dev/null +++ b/src/main/java/com/l2jserver/datapack/custom/service/base/util/CommandProcessor.java @@ -0,0 +1,55 @@ +/* + * Copyright © 2004-2020 L2J DataPack + * + * This file is part of L2J DataPack. + * + * L2J DataPack is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * L2J DataPack is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +package com.l2jserver.datapack.custom.service.base.util; + +import java.util.Objects; + +/** + * Command processor. + * @author HorridoJoho + * @version 2.6.2.0 + */ +public final class CommandProcessor { + private String remaining; + + public CommandProcessor(String command) { + Objects.requireNonNull(command); + remaining = command; + } + + public boolean matchAndRemove(String... expectations) { + Objects.requireNonNull(expectations); + for (String expectation : expectations) { + Objects.requireNonNull(expectation); + if (!expectation.isEmpty() && remaining.startsWith(expectation)) { + remaining = remaining.substring(expectation.length()); + return true; + } + } + return false; + } + + public String[] splitRemaining(String regex) { + return remaining.split(regex); + } + + public String getRemaining() { + return remaining; + } +} diff --git a/src/main/java/com/l2jserver/datapack/custom/service/base/util/htmltmpls/HTMLTemplateFunc.java b/src/main/java/com/l2jserver/datapack/custom/service/base/util/htmltmpls/HTMLTemplateFunc.java new file mode 100644 index 0000000000..dc1a035fac --- /dev/null +++ b/src/main/java/com/l2jserver/datapack/custom/service/base/util/htmltmpls/HTMLTemplateFunc.java @@ -0,0 +1,88 @@ +/* + * Copyright © 2004-2020 L2J DataPack + * + * This file is part of L2J DataPack. + * + * L2J DataPack is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * L2J DataPack is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +package com.l2jserver.datapack.custom.service.base.util.htmltmpls; + +import java.util.Map; + +import com.l2jserver.gameserver.model.actor.instance.L2PcInstance; + +/** + * This class represents a template function.<br> + * It has a sequence start and end.<br> + * ----- Example -----<br> + * -- startSequence = "INC"<br> + * -- endSequence = "ENDINC"<br> + * We have a template file template.tmpl: [INC(template2.tmpl)ENDINC]<br> + * Now when the handlers {@link #handle(StringBuilder, L2PcInstance, Map, HTMLTemplateFunc[])} method is called, contents will contain "template2.tmpl"<br> + * @author HorridoJoho + * @version 2.6.2.0 + */ +public abstract class HTMLTemplateFunc { + /** how the function sequence starts */ + private final String sequenceStart; + /** how the function sequence ends */ + private final String sequenceEnd; + /** flag to determine if the template function needs processing of it's contents before contents are passed to the {@link #handle(StringBuilder, L2PcInstance, Map, HTMLTemplateFunc[])} method */ + private final boolean requiresPreprocessing; + + /** + * Protected constructor for template function implementations. In a template document + * @param sequenceStart how the template function sequence starts (in a template document you use [sequenceStart( + * @param sequenceEnd how the template function sequence starts (in a template document you use )sequenceEnd] + * @param requiresPreprocessing flag to determine if the template function needs processing of it's contents before contents are passed to the {@link #handle(StringBuilder, L2PcInstance, Map, HTMLTemplateFunc[])} + */ + protected HTMLTemplateFunc(String sequenceStart, String sequenceEnd, boolean requiresPreprocessing) { + this.sequenceStart = "[" + sequenceStart + "("; + this.sequenceEnd = ")" + sequenceEnd + "]"; + this.requiresPreprocessing = requiresPreprocessing; + } + + /** + * @return the sequence this function starts with in a template document + */ + public final String getSequenceStart() { + return sequenceStart; + } + + /** + * @return the sequence this function ends with in a template document + */ + public final String getSequenceEnd() { + return sequenceEnd; + } + + /** + * @return true when the handler needs the contents pre-processed by the template engine before it is passed to the {@link #handle(StringBuilder, L2PcInstance, Map, HTMLTemplateFunc[])} method, false otherwise + */ + public final boolean requiresPreprocessing() { + return requiresPreprocessing; + } + + /** + * Called by template parser to give the function the possibility to<br> + * create new placeholders and modify the contents of the function<br> + * in the template document. + * @param content the content which can be modified by the handler + * @param player the player the template is processed for + * @param placeholders the currently available placeholders as unmodifiable map + * @param funcs supported functions the template is parsed with + * @return placeholder to add to the currently available placeholders, added before the content is processed after this call, and removed again after content processing + */ + public abstract Map<String, HTMLTemplatePlaceholder> handle(StringBuilder content, L2PcInstance player, Map<String, HTMLTemplatePlaceholder> placeholders, HTMLTemplateFunc[] funcs); +} \ No newline at end of file diff --git a/src/main/java/com/l2jserver/datapack/custom/service/base/util/htmltmpls/HTMLTemplateParser.java b/src/main/java/com/l2jserver/datapack/custom/service/base/util/htmltmpls/HTMLTemplateParser.java new file mode 100644 index 0000000000..c6ffc9ad7d --- /dev/null +++ b/src/main/java/com/l2jserver/datapack/custom/service/base/util/htmltmpls/HTMLTemplateParser.java @@ -0,0 +1,154 @@ +/* + * Copyright © 2004-2020 L2J DataPack + * + * This file is part of L2J DataPack. + * + * L2J DataPack is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * L2J DataPack is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +package com.l2jserver.datapack.custom.service.base.util.htmltmpls; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import com.l2jserver.gameserver.cache.HtmCache; +import com.l2jserver.gameserver.model.actor.instance.L2PcInstance; + +/** + * HTML Template Parser. + * @author HorridoJoho + * @version 2.6.2.0 + */ +public final class HTMLTemplateParser { + /** pattern to find placeholder references */ + private static final Pattern PLACEHOLDER_PATTERN = Pattern.compile("%[a-zA-Z_][a-zA-Z0-9_]*(\\.[a-zA-Z_][a-zA-Z0-9_]*)*%"); + + public static String fromCache(String path, Map<String, HTMLTemplatePlaceholder> placeholders, HTMLTemplateFunc... funcs) { + return fromCache(path, null, placeholders, funcs); + } + + public static String fromCache(String path, L2PcInstance player, Map<String, HTMLTemplatePlaceholder> placeholders, HTMLTemplateFunc... funcs) { + String string = HtmCache.getInstance().getHtm(player.getHtmlPrefix(), path); + if (string == null) { + return null; + } + StringBuilder builder = new StringBuilder(string); + fromStringBuilder(builder, player, placeholders, funcs); + return builder.toString(); + } + + /** + * Method to process a template. The string is directly modified and will contain the results of the template processing. + * @param string the template content + * @param player the player the template is processed for + * @param placeholders a map of placeholder (map has to be modifiable) + * @param funcs the functions to use while processing the template + */ + public static void fromStringBuilder(StringBuilder string, L2PcInstance player, Map<String, HTMLTemplatePlaceholder> placeholders, HTMLTemplateFunc... funcs) { + if (string == null) { + return; + } + + int indexOfOffset = 0; + + while (indexOfOffset < (string.length() - 1)) { + // find the first position of a placeholder or a custom func + Matcher placeholderMatcher = PLACEHOLDER_PATTERN.matcher(string); + int nextFuncStartOffset = -1; + int nextFuncEndOffset = -1; + if (placeholderMatcher.find(indexOfOffset)) { + nextFuncStartOffset = placeholderMatcher.start(); + nextFuncEndOffset = placeholderMatcher.end(); + } + + HTMLTemplateFunc nextFunc = null; + for (HTMLTemplateFunc func : funcs) { + int funcOffset = string.indexOf(func.getSequenceStart(), indexOfOffset); + if ((funcOffset > -1) && ((nextFuncStartOffset == -1) || (funcOffset < nextFuncStartOffset))) { + nextFuncStartOffset = funcOffset; + nextFuncEndOffset = HTMLTemplateUtils.findSequenceEnd(string, nextFuncStartOffset + func.getSequenceStart().length(), func); + nextFunc = func; + } + } + + if (nextFuncStartOffset == -1) { + break; + } else if (nextFunc == null) { + String placeholderString = placeholderMatcher.group().substring(1, placeholderMatcher.group().length() - 1); + HTMLTemplatePlaceholder placeholder = HTMLTemplateUtils.getPlaceholder(placeholderString, placeholders); + + if (placeholder != null) { + string.replace(nextFuncStartOffset, nextFuncEndOffset, placeholder.getValue()); + // 2 !!! placeholder replacement can contain more placeholders and func sequences start so we set the index to search to the start of the placeholder + indexOfOffset = nextFuncStartOffset; + } else + // skip placeholder? + { + // if placeholder can not be found, just remove it from the string + // 1 <<< string.delete(nextFuncStartOffset, nextFuncEndOffset); + + // 2 !!! l2j compatible mode, we don't want to manually add things like %objectId% placeholders all the time + indexOfOffset += placeholderMatcher.end() - placeholderMatcher.start(); + } + + // placeholder replacement can contain more placeholders and func sequences start so we set the index to search to the start of the placeholder + // 1 <<< indexOfOffset = nextFuncStartOffset; + } else { + if (nextFuncEndOffset == -1) { + // this is to ignore the starting sequences which have no ending sequence + ++indexOfOffset; + } else { + StringBuilder content = new StringBuilder(string.subSequence(nextFuncStartOffset + nextFunc.getSequenceStart().length(), nextFuncEndOffset - nextFunc.getSequenceEnd().length())); + + // the func needs pre-processing? + if (nextFunc.requiresPreprocessing()) { + fromStringBuilder(content, player, placeholders, funcs); + } + + Map<String, HTMLTemplatePlaceholder> tmpPlaceholders = nextFunc.handle(content, player, placeholders == null ? null : Collections.unmodifiableMap(placeholders), funcs); + + // add new entries and replace entries(temp) + if (tmpPlaceholders != null) { + for (HTMLTemplatePlaceholder newPlaceholder : tmpPlaceholders.values()) { + if (placeholders == null) { + placeholders = new HashMap<>(); + } + tmpPlaceholders.put(newPlaceholder.getName(), placeholders.put(newPlaceholder.getName(), newPlaceholder)); + } + } + + fromStringBuilder(content, player, placeholders, funcs); + string.replace(nextFuncStartOffset, nextFuncEndOffset, content.toString()); + + // remove entries which were new and restore old entries + if ((tmpPlaceholders != null) && (placeholders != null)) { + for (Entry<String, HTMLTemplatePlaceholder> oldPlaceholder : tmpPlaceholders.entrySet()) { + if (oldPlaceholder.getValue() == null) { + placeholders.remove(oldPlaceholder.getKey()); + } else { + placeholders.put(oldPlaceholder.getKey(), oldPlaceholder.getValue()); + } + } + } + + // set the current offset to the next func sequence start found, replaced content can contain more placeholders and funcs + indexOfOffset = nextFuncStartOffset; + } + } + } + } +} \ No newline at end of file diff --git a/src/main/java/com/l2jserver/datapack/custom/service/base/util/htmltmpls/HTMLTemplatePlaceholder.java b/src/main/java/com/l2jserver/datapack/custom/service/base/util/htmltmpls/HTMLTemplatePlaceholder.java new file mode 100644 index 0000000000..d513cb7793 --- /dev/null +++ b/src/main/java/com/l2jserver/datapack/custom/service/base/util/htmltmpls/HTMLTemplatePlaceholder.java @@ -0,0 +1,123 @@ +/* + * Copyright © 2004-2020 L2J DataPack + * + * This file is part of L2J DataPack. + * + * L2J DataPack is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * L2J DataPack is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +package com.l2jserver.datapack.custom.service.base.util.htmltmpls; + +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * This is the class for the built-in value placeholder.<br> + * It has a name, a value and can contain child placeholder.<br> + * To reference the value of a placeholder in a template document<br> + * you use <b>%placeholder_name%</b>. To reference the value of a child<br> + * placeholder you use <b>%placeholder_name.child_placeholder_name%.</b> + * @author HorridoJoho + * @version 2.6.2.0 + */ +public final class HTMLTemplatePlaceholder { + /** the name of this placeholder */ + private final String name; + /** the value of this placeholder */ + private volatile String value; + /** the child placeholders of this placeholder */ + private final Map<String, HTMLTemplatePlaceholder> children; + + /** + * Public constructor to create a new placeholder + * @param name the name of the new placeholder + * @param value the value of the new placeholder + */ + public HTMLTemplatePlaceholder(String name, String value) { + this(name, value, new LinkedHashMap<>()); + } + + /** + * Private constructor to create alias placeholders of other placeholders + * @param name the name of the alias placeholder + * @param value the value of the alias placeholder + * @param children the children of the alias placeholder + */ + private HTMLTemplatePlaceholder(String name, String value, Map<String, HTMLTemplatePlaceholder> children) { + this.name = name; + this.value = value; + this.children = children; + } + + /** + * Creates an alias for this placeholder.<br> + * An alias placeholder will hold the reference to the children map from the original placeholder. This means, adding a new child to the alias will also add the child to the original placeholder and vice versa. + * @param name name of the alias placeholder + * @return the newly created alias placeholder + */ + public HTMLTemplatePlaceholder createAlias(String name) { + return new HTMLTemplatePlaceholder(name, value, children); + } + + /** + * Adds a child placeholder to this placeholder. + * @param name the name of the new child placeholder + * @param value the value of the new child placeholder + * @return this placeholder + */ + public HTMLTemplatePlaceholder addChild(String name, String value) { + children.put(name, new HTMLTemplatePlaceholder(name, value)); + return this; + } + + public HTMLTemplatePlaceholder addAliasChild(String aliasName, HTMLTemplatePlaceholder placeholder) { + children.put(aliasName, placeholder.createAlias(aliasName)); + return this; + } + + public void setValue(String value) { + this.value = value; + } + + public String getName() { + return name; + } + + public String getValue() { + return value; + } + + /** + * Method to get a child placeholder of this placeholder by name + * @param name the name of the child placeholder to find + * @return the child placeholder + */ + public HTMLTemplatePlaceholder getChild(String name) { + return HTMLTemplateUtils.getPlaceholder(name, children); + } + + /** + * @return the child placeholder map of this placeholder as unmodifiable map + */ + public Map<String, HTMLTemplatePlaceholder> getChildren() { + return Collections.unmodifiableMap(children); + } + + /** + * @return the count of child placeholders in this placeholder + */ + public int getChildrenSize() { + return children.size(); + } +} \ No newline at end of file diff --git a/src/main/java/com/l2jserver/datapack/custom/service/base/util/htmltmpls/HTMLTemplateUtils.java b/src/main/java/com/l2jserver/datapack/custom/service/base/util/htmltmpls/HTMLTemplateUtils.java new file mode 100644 index 0000000000..506134ab49 --- /dev/null +++ b/src/main/java/com/l2jserver/datapack/custom/service/base/util/htmltmpls/HTMLTemplateUtils.java @@ -0,0 +1,129 @@ +/* + * Copyright © 2004-2020 L2J DataPack + * + * This file is part of L2J DataPack. + * + * L2J DataPack is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * L2J DataPack is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +package com.l2jserver.datapack.custom.service.base.util.htmltmpls; + +import java.util.Map; +import java.util.regex.Pattern; + +/** + * HTML Template Utils. + * @author HorridoJoho + * @version 2.6.2.0 + */ +public final class HTMLTemplateUtils { + public static int findSequenceEnd(StringBuilder string, int startOffset, HTMLTemplateFunc func) { + int dept = 0; + String seqStart = func.getSequenceStart(); + String seqEnd = func.getSequenceEnd(); + String escapedSeqStart = "\\" + seqStart; + String escapedSeqEnd = "\\" + seqEnd; + + while (true) { + int endSeqOffset = string.indexOf(seqEnd, startOffset); + if (endSeqOffset == -1) { + return -1; // there is no sequence end to find + } + + int escapedSeqStartOffset = string.indexOf(escapedSeqStart, startOffset); + int escapedSeqEndOffset = string.indexOf(escapedSeqEnd, startOffset); + int startSeqOffset = string.indexOf(seqStart, startOffset); + + if (((endSeqOffset < startSeqOffset) || (startSeqOffset == -1)) && ((endSeqOffset < escapedSeqStartOffset) || (escapedSeqStartOffset == -1)) && ((endSeqOffset < escapedSeqEndOffset) || (escapedSeqEndOffset == -1))) { + if (dept == 0) { + return endSeqOffset + seqEnd.length(); + } + + --dept; + startOffset = endSeqOffset + seqEnd.length(); + } else if ((startSeqOffset != -1) && (startSeqOffset < endSeqOffset) && ((startSeqOffset < escapedSeqStartOffset) || (escapedSeqStartOffset == -1)) && ((startSeqOffset < escapedSeqEndOffset) || (escapedSeqEndOffset == -1))) { + startOffset = startSeqOffset + seqStart.length(); + ++dept; + } else if ((escapedSeqStartOffset != -1) && ((escapedSeqStartOffset < escapedSeqEndOffset) || (escapedSeqEndOffset == -1))) { + startOffset += escapedSeqStartOffset + escapedSeqStart.length(); + } else if ((escapedSeqEndOffset != -1) && ((escapedSeqEndOffset < escapedSeqStartOffset) || (escapedSeqStartOffset != -1))) { + startOffset += escapedSeqEndOffset + escapedSeqEnd.length(); + } + } + } + + /** + * Searches the placeholder specified by placeholderString inside the placeholders map + * @param placeholderString placeholder to search for + * @param placeholders map with the placeholders available + * @return the placeholder if found, null otherwise + */ + public static HTMLTemplatePlaceholder getPlaceholder(String placeholderString, Map<String, HTMLTemplatePlaceholder> placeholders) { + if (placeholders == null) { + return null; + } + + String[] placeholderParts = placeholderString.split(Pattern.quote(".")); + HTMLTemplatePlaceholder placeholder = null; + for (String placeholderPart : placeholderParts) { + if (placeholder == null) { + placeholder = placeholders.get(placeholderPart); + if (placeholder == null) { + break; + } + } else { + placeholder = placeholder.getChild(placeholderPart); + if (placeholder == null) { + break; + } + } + } + return placeholder; + } + + /** + * Get the value of the placeholder specified by placeholderString + * @param placeholderString the placeholder to get the value from + * @param placeholders placeholder map to search in + * @return the value of the found placeholder + * @throws Exception the placeholder was not found or the value is null + */ + public static String getPlaceholderValue(String placeholderString, Map<String, HTMLTemplatePlaceholder> placeholders) throws Exception { + HTMLTemplatePlaceholder placeholder = getPlaceholder(placeholderString, placeholders); + if (placeholder == null) { + throw new Exception(); + } + + String value = placeholder.getValue(); + if (value == null) { + throw new Exception(); + } + + return value; + } + + /** + * Get the childs of the placeholder specified by placeholderString in an unmodifyable map + * @param placeholderString the placeholder to get the childs from + * @param placeholders placeholder map to search in + * @return the childs in an unmodifyable map of the found placeholder + * @throws Exception the placeholder was not found + */ + public static Map<String, HTMLTemplatePlaceholder> getPlaceholderChilds(String placeholderString, Map<String, HTMLTemplatePlaceholder> placeholders) throws Exception { + HTMLTemplatePlaceholder placeholder = getPlaceholder(placeholderString, placeholders); + if (placeholder == null) { + throw new Exception(); + } + return placeholder.getChildren(); + } +} diff --git a/src/main/java/com/l2jserver/datapack/custom/service/base/util/htmltmpls/funcs/ChildrenCountFunc.java b/src/main/java/com/l2jserver/datapack/custom/service/base/util/htmltmpls/funcs/ChildrenCountFunc.java new file mode 100644 index 0000000000..eae710443f --- /dev/null +++ b/src/main/java/com/l2jserver/datapack/custom/service/base/util/htmltmpls/funcs/ChildrenCountFunc.java @@ -0,0 +1,49 @@ +/* + * Copyright © 2004-2020 L2J DataPack + * + * This file is part of L2J DataPack. + * + * L2J DataPack is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * L2J DataPack is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +package com.l2jserver.datapack.custom.service.base.util.htmltmpls.funcs; + +import java.util.Map; + +import com.l2jserver.datapack.custom.service.base.util.htmltmpls.HTMLTemplateFunc; +import com.l2jserver.datapack.custom.service.base.util.htmltmpls.HTMLTemplatePlaceholder; +import com.l2jserver.datapack.custom.service.base.util.htmltmpls.HTMLTemplateUtils; +import com.l2jserver.gameserver.model.actor.instance.L2PcInstance; + +/** + * Children Count function. + * @author HorridoJoho + * @version 2.6.2.0 + */ +public final class ChildrenCountFunc extends HTMLTemplateFunc { + public static final ChildrenCountFunc INSTANCE = new ChildrenCountFunc(); + + private ChildrenCountFunc() { + super("CHILDSCOUNT", "ENDCHILDSCOUNT", false); + } + + @Override + public Map<String, HTMLTemplatePlaceholder> handle(StringBuilder content, L2PcInstance player, Map<String, HTMLTemplatePlaceholder> placeholders, HTMLTemplateFunc[] funcs) { + HTMLTemplatePlaceholder placeholder = HTMLTemplateUtils.getPlaceholder(content.toString(), placeholders); + content.setLength(0); + if (placeholder != null) { + content.append(placeholder.getChildrenSize()); + } + return null; + } +} diff --git a/src/main/java/com/l2jserver/datapack/custom/service/base/util/htmltmpls/funcs/ExistsFunc.java b/src/main/java/com/l2jserver/datapack/custom/service/base/util/htmltmpls/funcs/ExistsFunc.java new file mode 100644 index 0000000000..25b4dca896 --- /dev/null +++ b/src/main/java/com/l2jserver/datapack/custom/service/base/util/htmltmpls/funcs/ExistsFunc.java @@ -0,0 +1,85 @@ +/* + * Copyright © 2004-2020 L2J DataPack + * + * This file is part of L2J DataPack. + * + * L2J DataPack is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * L2J DataPack is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +package com.l2jserver.datapack.custom.service.base.util.htmltmpls.funcs; + +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import com.l2jserver.datapack.custom.service.base.util.htmltmpls.HTMLTemplateFunc; +import com.l2jserver.datapack.custom.service.base.util.htmltmpls.HTMLTemplatePlaceholder; +import com.l2jserver.datapack.custom.service.base.util.htmltmpls.HTMLTemplateUtils; +import com.l2jserver.gameserver.model.actor.instance.L2PcInstance; + +/** + * Exists function. + * @author HorridoJoho + * @version 2.6.2.0 + */ +public final class ExistsFunc extends HTMLTemplateFunc { + public static final ExistsFunc INSTANCE = new ExistsFunc(); + + private static final Pattern NEGATE_PATTERN = Pattern.compile("\\s*!\\s*"); + private static final Pattern PLACEHOLDER_PATTERN = Pattern.compile("[a-zA-Z_][a-zA-Z0-9_]*(\\.[a-zA-Z_][a-zA-Z0-9_]*)*\\s*,"); + + private ExistsFunc() { + super("EXISTS", "ENDEXISTS", false); + } + + @Override + public Map<String, HTMLTemplatePlaceholder> handle(StringBuilder content, L2PcInstance player, Map<String, HTMLTemplatePlaceholder> placeholders, HTMLTemplateFunc[] funcs) { + try { + boolean negate = false; + Matcher m = null; + + try { + m = getMatcher(NEGATE_PATTERN, content, 0); + negate = true; + } catch (Exception e) { + // ignore this exception, negate is optional + } + + if (m != null) { + m = getMatcher(PLACEHOLDER_PATTERN, content, m.end()); + } else { + m = getMatcher(PLACEHOLDER_PATTERN, content, 0); + } + + HTMLTemplatePlaceholder placeholder = HTMLTemplateUtils.getPlaceholder(m.group().substring(0, m.group().length() - 1).trim(), placeholders); + if (((placeholder == null) && !negate) || ((placeholder != null) && negate)) { + content.setLength(0); + return null; + } + + content.delete(0, m.end()); + } catch (Exception e) { + content.setLength(0); + } + return null; + } + + private static Matcher getMatcher(Pattern pattern, StringBuilder content, int findIndex) throws Exception { + Matcher m = pattern.matcher(content); + if (!m.find(findIndex) || (m.start() > findIndex)) { + throw new Exception(); + } + + return m; + } +} diff --git a/src/main/java/com/l2jserver/datapack/custom/service/base/util/htmltmpls/funcs/ForeachFunc.java b/src/main/java/com/l2jserver/datapack/custom/service/base/util/htmltmpls/funcs/ForeachFunc.java new file mode 100644 index 0000000000..8fda839d28 --- /dev/null +++ b/src/main/java/com/l2jserver/datapack/custom/service/base/util/htmltmpls/funcs/ForeachFunc.java @@ -0,0 +1,102 @@ +/* + * Copyright © 2004-2020 L2J DataPack + * + * This file is part of L2J DataPack. + * + * L2J DataPack is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * L2J DataPack is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +package com.l2jserver.datapack.custom.service.base.util.htmltmpls.funcs; + +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import com.l2jserver.datapack.custom.service.base.util.htmltmpls.HTMLTemplateFunc; +import com.l2jserver.datapack.custom.service.base.util.htmltmpls.HTMLTemplateParser; +import com.l2jserver.datapack.custom.service.base.util.htmltmpls.HTMLTemplatePlaceholder; +import com.l2jserver.datapack.custom.service.base.util.htmltmpls.HTMLTemplateUtils; +import com.l2jserver.gameserver.model.actor.instance.L2PcInstance; + +/** + * This class implements the following function syntax:<br> + * [FOREACH(alias_placeholder_name IN placeholder_name DO text per iteration)ENDEACH]<br> + * <br> + * This construct is able to iterate over the children of the "placeholder_name" placeholder.<br> + * For each child in this placeholder, the text after "DO" is placed in the content.<br> + * The current child is placed as an alias toplevel placeholder. This means, in this example<br> + * you can use %alias_placeholder_name% inside the foreach block. + * @author HorridoJoho + * @version 2.6.2.0 + */ +public final class ForeachFunc extends HTMLTemplateFunc { + public static final ForeachFunc INSTANCE = new ForeachFunc(); + + private static final Pattern FIRST_PLACEHOLDER_PATTERN = Pattern.compile("\\s*[a-zA-Z_][a-zA-Z0-9_]*(\\.[a-zA-Z_][a-zA-Z0-9_]*)*"); + + private static final Pattern IN_PATTERN = Pattern.compile("\\s*\\sIN\\s"); + + private static final Pattern SECOND_PLACEHOLDER_PATTERN = Pattern.compile("\\s*[a-zA-Z_][a-zA-Z0-9_]*(\\.[a-zA-Z_][a-zA-Z0-9_]*)*"); + + private static final Pattern DO_PATTERN = Pattern.compile("\\s*\\sDO\\s"); + + private ForeachFunc() { + super("FOREACH", "ENDEACH", false); + } + + @Override + public Map<String, HTMLTemplatePlaceholder> handle(StringBuilder content, L2PcInstance player, Map<String, HTMLTemplatePlaceholder> placeholders, HTMLTemplateFunc[] funcs) { + try { + Matcher matcher = getMatcher(FIRST_PLACEHOLDER_PATTERN, content, 0); + String aliasPlaceholderName = matcher.group().trim(); + int findIndex = matcher.end(); + + matcher = getMatcher(IN_PATTERN, content, findIndex); + findIndex = matcher.end(); + + matcher = getMatcher(SECOND_PLACEHOLDER_PATTERN, content, findIndex); + Map<String, HTMLTemplatePlaceholder> childPlaceholders = HTMLTemplateUtils.getPlaceholderChilds(matcher.group().trim(), placeholders); + findIndex = matcher.end(); + + matcher = getMatcher(DO_PATTERN, content, findIndex); + findIndex = matcher.end(); + + content.delete(0, findIndex); + HashMap<String, HTMLTemplatePlaceholder> newPlaceholders = new HashMap<>(placeholders); + StringBuilder orgContent = new StringBuilder(content); + StringBuilder modContent = new StringBuilder(content.length()); + content.setLength(0); + // we don't need to save an overwritten placeholder, we create our own map + for (Entry<String, HTMLTemplatePlaceholder> childPlaceholder : childPlaceholders.entrySet()) { + modContent.setLength(0); + modContent.append(orgContent); + newPlaceholders.put(aliasPlaceholderName, childPlaceholder.getValue().createAlias(aliasPlaceholderName)); + HTMLTemplateParser.fromStringBuilder(modContent, player, newPlaceholders, funcs); + content.append(modContent); + } + } catch (Exception ex) { + content.setLength(0); + } + return null; + } + + private static Matcher getMatcher(Pattern pattern, StringBuilder content, int findIndex) throws Exception { + Matcher m = pattern.matcher(content); + if (!m.find(findIndex) || (m.start() > findIndex)) { + throw new Exception(); + } + return m; + } +} diff --git a/src/main/java/com/l2jserver/datapack/custom/service/base/util/htmltmpls/funcs/IfChildrenFunc.java b/src/main/java/com/l2jserver/datapack/custom/service/base/util/htmltmpls/funcs/IfChildrenFunc.java new file mode 100644 index 0000000000..c63c491863 --- /dev/null +++ b/src/main/java/com/l2jserver/datapack/custom/service/base/util/htmltmpls/funcs/IfChildrenFunc.java @@ -0,0 +1,144 @@ +/* + * Copyright © 2004-2020 L2J DataPack + * + * This file is part of L2J DataPack. + * + * L2J DataPack is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * L2J DataPack is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +package com.l2jserver.datapack.custom.service.base.util.htmltmpls.funcs; + +import java.util.Map; +import java.util.Map.Entry; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import com.l2jserver.datapack.custom.service.base.util.htmltmpls.HTMLTemplateFunc; +import com.l2jserver.datapack.custom.service.base.util.htmltmpls.HTMLTemplatePlaceholder; +import com.l2jserver.datapack.custom.service.base.util.htmltmpls.HTMLTemplateUtils; +import com.l2jserver.gameserver.model.actor.instance.L2PcInstance; + +/** + * If Children function. + * @author HorridoJoho + * @version 2.6.2.0 + */ +public final class IfChildrenFunc extends HTMLTemplateFunc { + public static final IfChildrenFunc INSTANCE = new IfChildrenFunc(); + + private static final Pattern CHILDREN_OF_PLACEHOLDER_PATTERN = Pattern.compile("\\s*[a-zA-Z_][a-zA-Z0-9_]*(\\.[a-zA-Z_][a-zA-Z0-9_]*)*\\s*,"); + + private static final Pattern CHILD_PLACEHOLDER_PATTERN = Pattern.compile("\\s*[a-zA-Z_][a-zA-Z0-9_]*(\\.[a-zA-Z_][a-zA-Z0-9_]*)*"); + + private static final Pattern OP_PATTERN = Pattern.compile("\\s*(<|>|<=|>=|==|!=|\\sENDS_WITH\\s|\\sSTARTS_WITH\\s)\\s*"); + + private static final Pattern RVALUE_PATTERN = Pattern.compile("([a-zA-Z_][a-zA-Z0-9_]*(\\.[a-zA-Z_][a-zA-Z0-9_]*)*|\"(\\\\.|\\\\\\s|\\s|[^\\\\\"])*\")"); + + private static final Pattern THEN_PATTERN = Pattern.compile("\\s*\\sTHEN\\s"); + + private IfChildrenFunc() { + super("IFCHILDS", "ENDIFCHILDS", false); + } + + @Override + public Map<String, HTMLTemplatePlaceholder> handle(StringBuilder content, L2PcInstance player, Map<String, HTMLTemplatePlaceholder> placeholders, HTMLTemplateFunc[] funcs) { + try { + Matcher matcher = getMatcher(CHILDREN_OF_PLACEHOLDER_PATTERN, content, 0); + String childrenPlaceholderString = matcher.group().substring(0, matcher.group().length() - 1); + HTMLTemplatePlaceholder childrenPlaceholder = HTMLTemplateUtils.getPlaceholder(childrenPlaceholderString, placeholders); + if (childrenPlaceholder == null) { + content.setLength(0); + return null; + } + + matcher = getMatcher(CHILD_PLACEHOLDER_PATTERN, content, matcher.end()); + String childPlaceholderString = matcher.group().trim(); + int findIndex = matcher.end(); + + matcher = getMatcher(OP_PATTERN, content, findIndex); + String op = matcher.group().trim(); + findIndex = matcher.end(); + + matcher = getMatcher(RVALUE_PATTERN, content, findIndex); + String rValue = matcher.group(); + if (rValue.charAt(0) == '"') { + rValue = rValue.substring(1, rValue.length() - 1); + } else { + rValue = HTMLTemplateUtils.getPlaceholderValue(rValue, placeholders); + } + findIndex = matcher.end(); + + matcher = getMatcher(THEN_PATTERN, content, findIndex); + findIndex = matcher.end(); + + for (Entry<String, HTMLTemplatePlaceholder> entry : childrenPlaceholder.getChildren().entrySet()) { + HTMLTemplatePlaceholder childPlaceholder = entry.getValue().getChild(childPlaceholderString); + if (childPlaceholder == null) { + continue; + } + + try { + boolean ok = false; + switch (op) { + case "<": + ok = Integer.parseInt(childPlaceholder.getValue()) < Integer.parseInt(rValue); + break; + case ">": + ok = Integer.parseInt(childPlaceholder.getValue()) > Integer.parseInt(rValue); + break; + case "<=": + ok = Integer.parseInt(childPlaceholder.getValue()) <= Integer.parseInt(rValue); + break; + case ">=": + ok = Integer.parseInt(childPlaceholder.getValue()) >= Integer.parseInt(rValue); + break; + case "==": + ok = childPlaceholder.getValue().equals(rValue); + break; + case "!=": + ok = !childPlaceholder.getValue().equals(rValue); + break; + case "ENDS_WITH": + ok = childPlaceholder.getValue().endsWith(rValue); + break; + case "STARTS_WITH": + ok = childPlaceholder.getValue().startsWith(rValue); + break; + } + + if (!ok) { + // condition is not met, no content to show + content.setLength(0); + return null; + } + } catch (Exception e) { + // on an exception the types are incompatible with the operator, this function ignores such cases + } + } + + content.delete(0, findIndex); + } catch (Exception ex) { + content.setLength(0); + } + return null; + } + + private static Matcher getMatcher(Pattern pattern, StringBuilder content, int findIndex) throws Exception { + Matcher m = pattern.matcher(content); + if (!m.find(findIndex) || (m.start() > findIndex)) { + throw new Exception(); + } + + return m; + } +} diff --git a/src/main/java/com/l2jserver/datapack/custom/service/base/util/htmltmpls/funcs/IfFunc.java b/src/main/java/com/l2jserver/datapack/custom/service/base/util/htmltmpls/funcs/IfFunc.java new file mode 100644 index 0000000000..47eb87fb31 --- /dev/null +++ b/src/main/java/com/l2jserver/datapack/custom/service/base/util/htmltmpls/funcs/IfFunc.java @@ -0,0 +1,136 @@ +/* + * Copyright © 2004-2020 L2J DataPack + * + * This file is part of L2J DataPack. + * + * L2J DataPack is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * L2J DataPack is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +package com.l2jserver.datapack.custom.service.base.util.htmltmpls.funcs; + +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import com.l2jserver.datapack.custom.service.base.util.htmltmpls.HTMLTemplateFunc; +import com.l2jserver.datapack.custom.service.base.util.htmltmpls.HTMLTemplatePlaceholder; +import com.l2jserver.datapack.custom.service.base.util.htmltmpls.HTMLTemplateUtils; +import com.l2jserver.gameserver.model.actor.instance.L2PcInstance; + +/** + * If function. + * This class implements the following function syntax:<br> + * [IF(placeholder_name == "text in string" THEN text when the condition matches)ENDIF]<br> + * [IF(placeholder_name == another_placeholder_name THEN text when the condition matches)ENDIF]<br> + * [IF(int_placeholder_name > another_int_placeholder_name THEN text when the condition matches)ENDIF]<br> + * <br> + * First is always a placeholder name.<br> + * <br> + * Second comes one of the following operators:<br> + * <, >, <=, >=, !=, ==, STARTS_WITH or ENDS_WITH, where <, >, <= and >= are only to be used with placeholders/strings<br> + * which have a numeric value.<br> + * <br> + * Third comes either a placeholder name or a string("text in string").<br> + * <br> + * After the "THEN" word comes the text to place in the content when the condition is met. + * @author HorridoJoho + * @version 2.6.2.0 + */ +public final class IfFunc extends HTMLTemplateFunc { + public static final IfFunc INSTANCE = new IfFunc(); + + private static final Pattern LVALUE_PATTERN = Pattern.compile("\\s*[a-zA-Z_][a-zA-Z0-9_]*(\\.[a-zA-Z_][a-zA-Z0-9_]*)*"); + + private static final Pattern OP_PATTERN = Pattern.compile("\\s*(<|>|<=|>=|==|!=|\\sENDS_WITH\\s|\\sSTARTS_WITH\\s)\\s*"); + + private static final Pattern RVALUE_PATTERN = Pattern.compile("([a-zA-Z_][a-zA-Z0-9_]*(\\.[a-zA-Z_][a-zA-Z0-9_]*)*|\"(\\\\.|\\\\\\s|\\s|[^\\\\\"])*\")"); + + private static final Pattern THEN_PATTERN = Pattern.compile("\\s*\\sTHEN\\s"); + + private IfFunc() { + super("IF", "ENDIF", false); + } + + @Override + public Map<String, HTMLTemplatePlaceholder> handle(StringBuilder content, L2PcInstance player, Map<String, HTMLTemplatePlaceholder> placeholders, HTMLTemplateFunc[] funcs) { + try { + Matcher matcher = getMatcher(LVALUE_PATTERN, content, 0); + String lValue = HTMLTemplateUtils.getPlaceholderValue(matcher.group().trim(), placeholders); + int findIndex = matcher.end(); + + matcher = getMatcher(OP_PATTERN, content, findIndex); + String op = matcher.group().trim(); + findIndex = matcher.end(); + + matcher = getMatcher(RVALUE_PATTERN, content, findIndex); + String rValue = matcher.group(); + if (rValue.charAt(0) == '"') { + rValue = rValue.substring(1, rValue.length() - 1); + } else { + rValue = HTMLTemplateUtils.getPlaceholderValue(rValue, placeholders); + } + findIndex = matcher.end(); + + matcher = getMatcher(THEN_PATTERN, content, findIndex); + findIndex = matcher.end(); + + boolean ok = false; + switch (op) { + case "<": + ok = Integer.parseInt(lValue) < Integer.parseInt(rValue); + break; + case ">": + ok = Integer.parseInt(lValue) > Integer.parseInt(rValue); + break; + case "<=": + ok = Integer.parseInt(lValue) <= Integer.parseInt(rValue); + break; + case ">=": + ok = Integer.parseInt(lValue) >= Integer.parseInt(rValue); + break; + case "==": + ok = lValue.equals(rValue); + break; + case "!=": + ok = !lValue.equals(rValue); + break; + case "ENDS_WITH": + ok = lValue.endsWith(rValue); + break; + case "STARTS_WITH": + ok = lValue.startsWith(rValue); + break; + } + + if (ok) { + // this ensures only the replacement content is left + content.delete(0, findIndex); + } else { + // condition is not met, no content to show + content.setLength(0); + return null; + } + } catch (Exception ex) { + content.setLength(0); + } + return null; + } + + private static Matcher getMatcher(Pattern pattern, StringBuilder content, int findIndex) throws Exception { + Matcher m = pattern.matcher(content); + if (!m.find(findIndex) || (m.start() > findIndex)) { + throw new Exception(); + } + return m; + } +} diff --git a/src/main/java/com/l2jserver/datapack/custom/service/base/util/htmltmpls/funcs/IncludeFunc.java b/src/main/java/com/l2jserver/datapack/custom/service/base/util/htmltmpls/funcs/IncludeFunc.java new file mode 100644 index 0000000000..14a55d20a0 --- /dev/null +++ b/src/main/java/com/l2jserver/datapack/custom/service/base/util/htmltmpls/funcs/IncludeFunc.java @@ -0,0 +1,50 @@ +/* + * Copyright © 2004-2020 L2J DataPack + * + * This file is part of L2J DataPack. + * + * L2J DataPack is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * L2J DataPack is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +package com.l2jserver.datapack.custom.service.base.util.htmltmpls.funcs; + +import java.util.Map; + +import com.l2jserver.datapack.custom.service.base.util.htmltmpls.HTMLTemplateFunc; +import com.l2jserver.datapack.custom.service.base.util.htmltmpls.HTMLTemplatePlaceholder; +import com.l2jserver.gameserver.cache.HtmCache; +import com.l2jserver.gameserver.model.actor.instance.L2PcInstance; + +/** + * Include function. + * @author HorridoJoho + * @version 2.6.2.0 + */ +public final class IncludeFunc extends HTMLTemplateFunc { + public static final IncludeFunc INSTANCE = new IncludeFunc(); + + private IncludeFunc() { + super("INC", "ENDINC", true); + } + + @Override + public Map<String, HTMLTemplatePlaceholder> handle(StringBuilder content, L2PcInstance player, Map<String, HTMLTemplatePlaceholder> placeholders, HTMLTemplateFunc[] funcs) { + String fileContent = HtmCache.getInstance().getHtm(player != null ? player.getHtmlPrefix() : null, content.toString()); + if (fileContent != null) { + content.ensureCapacity(fileContent.length()); + content.setLength(0); + content.append(fileContent); + } + return null; + } +} diff --git a/src/main/java/com/l2jserver/datapack/custom/service/buffer/BufferService.java b/src/main/java/com/l2jserver/datapack/custom/service/buffer/BufferService.java new file mode 100644 index 0000000000..14faec5607 --- /dev/null +++ b/src/main/java/com/l2jserver/datapack/custom/service/buffer/BufferService.java @@ -0,0 +1,539 @@ +/* + * Copyright © 2004-2020 L2J DataPack + * + * This file is part of L2J DataPack. + * + * L2J DataPack is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * L2J DataPack is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +package com.l2jserver.datapack.custom.service.buffer; + +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.concurrent.ConcurrentHashMap; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.l2jserver.datapack.custom.service.base.CustomServiceScript; +import com.l2jserver.datapack.custom.service.base.util.CommandProcessor; +import com.l2jserver.datapack.custom.service.base.util.htmltmpls.HTMLTemplatePlaceholder; +import com.l2jserver.datapack.custom.service.buffer.model.entity.AbstractBuffer; +import com.l2jserver.datapack.custom.service.buffer.model.entity.BuffCategory; +import com.l2jserver.datapack.custom.service.buffer.model.entity.BuffSkill; +import com.l2jserver.gameserver.config.Configuration; +import com.l2jserver.gameserver.handler.BypassHandler; +import com.l2jserver.gameserver.handler.ItemHandler; +import com.l2jserver.gameserver.handler.VoicedCommandHandler; +import com.l2jserver.gameserver.model.actor.L2Npc; +import com.l2jserver.gameserver.model.actor.L2Playable; +import com.l2jserver.gameserver.model.actor.instance.L2PcInstance; +import com.l2jserver.gameserver.model.skills.BuffInfo; + +/** + * Buffer Service. + * @author HorridoJoho + * @version 2.6.2.0 + */ +public final class BufferService extends CustomServiceScript { + + private static final Logger LOG = LoggerFactory.getLogger(BufferService.class); + + public static final String SCRIPT_NAME = "buffer"; + + public static final Path SCRIPT_PATH = Paths.get(SCRIPT_COLLECTION, SCRIPT_NAME); + + public static void main(String[] args) { + if (!Configuration.bufferService().enable()) { + LOG.info("Disabled."); + return; + } + + LOG.info("Loading..."); + + BufferServiceRepository.getInstance().getConfig().registerNpcs(getInstance()); + } + + private static final Map<Integer, Long> LAST_PLAYABLES_HEAL_TIME = new ConcurrentHashMap<>(); + + private static final Map<Integer, String> ACTIVE_PLAYER_BUFFLISTS = new ConcurrentHashMap<>(); + + BufferService() { + super(SCRIPT_NAME); + + BypassHandler.getInstance().registerHandler(BufferServiceBypassHandler.getInstance()); + + if (Configuration.bufferService().getVoicedEnable()) { + VoicedCommandHandler.getInstance().registerHandler(BufferServiceVoicedCommandHandler.getInstance()); + ItemHandler.getInstance().registerHandler(BufferServiceItemHandler.getInstance()); + } + } + + @Override + public boolean unload() { + BypassHandler.getInstance().removeHandler(BufferServiceBypassHandler.getInstance()); + if (Configuration.bufferService().getVoicedEnable()) { + VoicedCommandHandler.getInstance().removeHandler(BufferServiceVoicedCommandHandler.getInstance()); + ItemHandler.getInstance().removeHandler(BufferServiceItemHandler.getInstance()); + } + return super.unload(); + } + + private void castBuff(L2Playable playable, BuffSkill buff) { + buff.getSkill().applyEffects(playable, playable); + } + + private void showAdvancedHtml(L2PcInstance player, AbstractBuffer buffer, L2Npc npc, String htmlPath, Map<String, HTMLTemplatePlaceholder> placeholders) { + HTMLTemplatePlaceholder ulistsPlaceholder = BufferServiceRepository.getInstance().getPlayersUListsPlaceholder(player.getObjectId()); + if (ulistsPlaceholder != null) { + placeholders.put(ulistsPlaceholder.getName(), ulistsPlaceholder); + } + + String activeUniqueName = ACTIVE_PLAYER_BUFFLISTS.get(player.getObjectId()); + if (activeUniqueName != null) { + HTMLTemplatePlaceholder ulistPlaceholder = BufferServiceRepository.getInstance().getPlayersUListPlaceholder(player.getObjectId(), activeUniqueName); + if (ulistPlaceholder != null) { + placeholders.put("active_unique", ulistPlaceholder); + } + } + + HTMLTemplatePlaceholder playerPlaceholder = new HTMLTemplatePlaceholder("player", null); + playerPlaceholder.addChild("name", player.getName()); + playerPlaceholder.addChild("unique_max_buffs", String.valueOf(player.getStat().getMaxBuffCount())); + playerPlaceholder.addChild("unique_max_songs_dances", String.valueOf(Configuration.character().getMaxDanceAmount())); + + placeholders.put(playerPlaceholder.getName(), playerPlaceholder); + + super.showAdvancedHtml(player, buffer, npc, htmlPath, placeholders); + } + + private boolean htmlShowMain(L2PcInstance player, AbstractBuffer buffer, L2Npc npc) { + showAdvancedHtml(player, buffer, npc, "main.html", new HashMap<>()); + return true; + } + + private boolean htmlShowCategory(L2PcInstance player, AbstractBuffer buffer, L2Npc npc, String categoryIdent) { + BuffCategory buffCat = buffer.getBuffCats().get(categoryIdent); + if (buffCat == null) { + debug(player, "Invalid buff category: " + categoryIdent); + return false; + } + + HashMap<String, HTMLTemplatePlaceholder> placeholders = new HashMap<>(); + + placeholders.put("category", buffCat.getPlaceholder()); + + showAdvancedHtml(player, buffer, npc, "category.html", placeholders); + return true; + } + + private boolean htmlShowBuff(L2PcInstance player, AbstractBuffer buffer, L2Npc npc, String categoryIdent, String buffIdent) { + BuffCategory buffCat = buffer.getBuffCats().get(categoryIdent); + if (buffCat == null) { + debug(player, "Invalid buff category: " + categoryIdent); + return false; + } + BuffSkill buff = buffCat.getBuff(buffIdent); + if (buff == null) { + debug(player, "Invalid buff skill: " + buffIdent); + return false; + } + + HashMap<String, HTMLTemplatePlaceholder> placeholders = new HashMap<>(); + + placeholders.put("category", buffCat.getPlaceholder()); + placeholders.put("buff", buff.getPlaceholder()); + + showAdvancedHtml(player, buffer, npc, "buff.html", placeholders); + return true; + } + + private boolean htmlShowPreset(L2PcInstance player, AbstractBuffer buffer, L2Npc npc, String presetBufflistIdent) { + BuffCategory presetBufflist = buffer.getPresetBuffCats().get(presetBufflistIdent); + if (presetBufflist == null) { + debug(player, "Invalid preset buff category: " + presetBufflistIdent); + return false; + } + + var placeholders = new HashMap<String, HTMLTemplatePlaceholder>(); + + placeholders.put("preset", presetBufflist.getPlaceholder()); + + showAdvancedHtml(player, buffer, npc, "preset.html", placeholders); + return true; + } + + private boolean htmlShowUnique(L2PcInstance player, AbstractBuffer buffer, L2Npc npc, String uniqueName) { + HTMLTemplatePlaceholder uniquePlaceholder = BufferServiceRepository.getInstance().getPlayersUListPlaceholder(player.getObjectId(), uniqueName); + if (uniquePlaceholder == null) { + // redirect to main html if uniqueName is not valid, will most likely happen when the player deletes a unique bufflist he is currently viewing + executeHtmlCommand(player, npc, new CommandProcessor("main")); + return false; + } + + HashMap<String, HTMLTemplatePlaceholder> placeholders = new HashMap<>(); + + placeholders.put(uniquePlaceholder.getName(), uniquePlaceholder); + + showAdvancedHtml(player, buffer, npc, "unique.html", placeholders); + return true; + } + + private void targetBuffBuff(L2PcInstance player, L2Playable target, AbstractBuffer buffer, String categoryIdent, String buffIdent) { + BuffCategory bCat = buffer.getBuffCats().get(categoryIdent); + if (bCat == null) { + debug(player, "Invalid buff category: " + categoryIdent); + return; + } + BuffSkill buff = bCat.getBuff(buffIdent); + if (buff == null) { + debug(player, "Invalid buff skill: " + buffIdent); + return; + } + + if (!buff.getItems().isEmpty()) { + HashMap<Integer, Long> items = new HashMap<>(); + fillItemAmountMap(items, buff); + + for (Entry<Integer, Long> item : items.entrySet()) { + if (player.getInventory().getInventoryItemCount(item.getKey(), 0, true) < item.getValue()) { + player.sendMessage("Not enough items!"); + return; + } + } + + for (Entry<Integer, Long> item : items.entrySet()) { + player.destroyItemByItemId("BufferService", item.getKey(), item.getValue(), player, true); + } + } + + castBuff(target, buff); + } + + private void targetBuffUnique(L2PcInstance player, L2Playable target, AbstractBuffer buffer, String uniqueName) { + List<BuffSkill> buffs = BufferServiceRepository.getInstance().getUniqueBufflist(player.getObjectId(), uniqueName); + + if (buffs != null) { + HashMap<Integer, Long> items = null; + for (BuffSkill buff : buffs) { + if (!buff.getItems().isEmpty()) { + if (items == null) { + items = new HashMap<>(); + } + fillItemAmountMap(items, buff); + } + } + + if (items != null) { + for (var item : items.entrySet()) { + if (player.getInventory().getInventoryItemCount(item.getKey(), 0, true) < item.getValue()) { + player.sendMessage("Not enough items!"); + return; + } + } + + for (var item : items.entrySet()) { + player.destroyItemByItemId("BufferService", item.getKey(), item.getValue(), player, true); + } + } + + for (BuffSkill buff : buffs) { + castBuff(target, buff); + } + } + } + + private void targetBuffPreset(L2PcInstance player, L2Playable target, AbstractBuffer buffer, String presetBufflistIdent) { + BuffCategory presetBufflist = buffer.getPresetBuffCats().get(presetBufflistIdent); + if (presetBufflist == null) { + debug(player, "Invalid preset buff category: " + presetBufflistIdent); + return; + } + + Collection<BuffSkill> buffs = presetBufflist.getBuffs().values(); + + if (buffs != null) { + HashMap<Integer, Long> items = null; + for (BuffSkill buff : buffs) { + if (!buff.getItems().isEmpty()) { + if (items == null) { + items = new HashMap<>(); + } + fillItemAmountMap(items, buff); + } + } + + if (items != null) { + for (Entry<Integer, Long> item : items.entrySet()) { + if (player.getInventory().getInventoryItemCount(item.getKey(), 0, true) < item.getValue()) { + player.sendMessage("Not enough items!"); + return; + } + } + + for (Entry<Integer, Long> item : items.entrySet()) { + player.destroyItemByItemId("BufferService", item.getKey(), item.getValue(), player, true); + } + } + + for (BuffSkill buff : buffs) { + castBuff(target, buff); + } + } + } + + private void targetHeal(L2PcInstance player, L2Playable target, AbstractBuffer buffer) { + if (!buffer.getCanHeal()) { + debug(player, "This buffer can not heal!"); + return; + } + + // prevent heal spamming, process cooldown on heal target + Long lastPlayableHealTime = LAST_PLAYABLES_HEAL_TIME.get(target.getObjectId()); + if (lastPlayableHealTime != null) { + Long elapsedTime = System.currentTimeMillis() - lastPlayableHealTime; + Long healCooldown = Configuration.bufferService().getHealCooldown(); + if (elapsedTime < healCooldown) { + long remainingTime = healCooldown - elapsedTime; + if (target == player) { + player.sendMessage("You can heal yourself again in " + (remainingTime / 1000) + " seconds."); + } else { + player.sendMessage("You can heal your pet again in " + (remainingTime / 1000) + " seconds."); + } + return; + } + } + + LAST_PLAYABLES_HEAL_TIME.put(target.getObjectId(), System.currentTimeMillis()); + + if (player == target) { + player.setCurrentCp(player.getMaxCp()); + } + target.setCurrentHp(target.getMaxHp()); + target.setCurrentMp(target.getMaxMp()); + target.broadcastStatusUpdate(); + } + + private void targetCancel(L2PcInstance player, L2Playable target, AbstractBuffer buffer) { + if (!buffer.getCanCancel()) { + debug(player, "This buffer can not cancel!"); + return; + } + target.stopAllEffectsExceptThoseThatLastThroughDeath(); + } + + private void executeTargetCommand(L2PcInstance player, AbstractBuffer buffer, CommandProcessor command) { + // first determine the target + L2Playable target; + if (command.matchAndRemove("player ", "p ")) { + target = player; + } else if (command.matchAndRemove("summon ", "s ")) { + target = player.getSummon(); + if (target == null) { + debug(player, "No summon available!"); + return; + } + } else { + debug(player, "Invalid target command target!"); + return; + } + + // run the chosen action on the target + if (command.matchAndRemove("buff ", "b ")) { + String[] argsSplit = command.splitRemaining(" "); + if (argsSplit.length != 2) { + debug(player, "Missing arguments!"); + return; + } + targetBuffBuff(player, target, buffer, argsSplit[0], argsSplit[1]); + } else if (command.matchAndRemove("unique ", "u ")) { + targetBuffUnique(player, target, buffer, command.getRemaining()); + } else if (command.matchAndRemove("preset ", "p ")) { + targetBuffPreset(player, target, buffer, command.getRemaining()); + } else if (command.matchAndRemove("heal", "h")) { + targetHeal(player, target, buffer); + } else if (command.matchAndRemove("cancel", "c")) { + targetCancel(player, target, buffer); + } + } + + private boolean uniqueCreate(L2PcInstance player, String uniqueName) { + if (!BufferServiceRepository.getInstance().canHaveMoreBufflists(player)) { + player.sendMessage("Maximum number of unique bufflists reached!"); + return false; + } + + // only allow alpha numeric names because we use this name on the htmls + if (!uniqueName.matches("[A-Za-z0-9]+")) { + return false; + } + + return BufferServiceRepository.getInstance().createUniqueBufflist(player.getObjectId(), uniqueName); + } + + private void uniqueDelete(L2PcInstance player, String uniqueName) { + BufferServiceRepository.getInstance().deleteUniqueBufflist(player.getObjectId(), uniqueName); + // also remove from active buff list when it's the deleted + String activeUniqueName = ACTIVE_PLAYER_BUFFLISTS.get(player.getObjectId()); + if ((activeUniqueName != null) && activeUniqueName.equals(uniqueName)) { + ACTIVE_PLAYER_BUFFLISTS.remove(player.getObjectId()); + } + } + + private void uniqueAdd(L2PcInstance player, AbstractBuffer buffer, String uniqueName, String categoryIdent, String buffIdent) { + BuffCategory bCat = buffer.getBuffCats().get(categoryIdent); + if (bCat == null) { + return; + } + BuffSkill buff = bCat.getBuff(buffIdent); + if (buff == null) { + return; + } + + BufferServiceRepository.getInstance().addToUniqueBufflist(player, uniqueName, buff); + } + + private void uniqueRemove(L2PcInstance player, String uniqueName, String buffIdent) { + BuffSkill buff = BufferServiceRepository.getInstance().getConfig().getGlobal().getBuff(buffIdent); + if (buff == null) { + return; + } + + BufferServiceRepository.getInstance().removeFromUniqueBufflist(player.getObjectId(), uniqueName, buff); + } + + private void uniqueSelect(L2PcInstance player, String uniqueName) { + if (BufferServiceRepository.getInstance().hasUniqueBufflist(player.getObjectId(), uniqueName)) { + ACTIVE_PLAYER_BUFFLISTS.put(player.getObjectId(), uniqueName); + } + } + + private void uniqueDeselect(L2PcInstance player) { + ACTIVE_PLAYER_BUFFLISTS.remove(player.getObjectId()); + } + + private void executeUniqueCommand(L2PcInstance player, AbstractBuffer buffer, CommandProcessor command) { + if (command.matchAndRemove("create ", "c ")) { + uniqueCreate(player, command.getRemaining()); + } else if (command.matchAndRemove("create_from_effects ", "cfe ")) { + String uniqueName = command.getRemaining(); + if (!uniqueCreate(player, uniqueName)) { + return; + } + + final List<BuffInfo> effects = player.getEffectList().getEffects(); + for (final BuffInfo effect : effects) { + for (Entry<String, BuffCategory> buffCatEntry : buffer.getBuffCats().entrySet()) { + boolean added = false; + + for (Entry<String, BuffSkill> buffEntry : buffCatEntry.getValue().getBuffs().entrySet()) { + final BuffSkill buff = buffEntry.getValue(); + + if (buff.getSkill().getId() == effect.getSkill().getId()) { + uniqueAdd(player, buffer, uniqueName, buffCatEntry.getKey(), buff.getId()); + added = true; + break; + } + } + + if (added) { + break; + } + } + } + } else if (command.matchAndRemove("delete ", "del ")) { + uniqueDelete(player, command.getRemaining()); + } else if (command.matchAndRemove("add ", "a ")) { + String[] argsSplit = command.splitRemaining(" "); + if (argsSplit.length != 3) { + debug(player, "Missing arguments!"); + return; + } + uniqueAdd(player, buffer, argsSplit[0], argsSplit[1], argsSplit[2]); + } else if (command.matchAndRemove("remove ", "r ")) { + String[] argsSplit = command.splitRemaining(" "); + if (argsSplit.length != 2) { + debug(player, "Missing arguments!"); + return; + } + uniqueRemove(player, argsSplit[0], argsSplit[1]); + } else if (command.matchAndRemove("select ", "s ")) { + uniqueSelect(player, command.getRemaining()); + } else if (command.matchAndRemove("deselect", "des")) { + uniqueDeselect(player); + } + } + + @Override + public boolean executeHtmlCommand(L2PcInstance player, L2Npc npc, CommandProcessor command) { + AbstractBuffer buffer = BufferServiceRepository.getInstance().getConfig().determineBuffer(npc, player); + if (buffer == null) { + player.sendMessage("No authorization!"); + return false; + } + + if (command.matchAndRemove("main", "m")) { + return htmlShowMain(player, buffer, npc); + } else if (command.matchAndRemove("category ", "c ")) { + return htmlShowCategory(player, buffer, npc, command.getRemaining()); + } else if (command.matchAndRemove("preset ", "p ")) { + return htmlShowPreset(player, buffer, npc, command.getRemaining()); + } else if (command.matchAndRemove("buff ", "b ")) { + String[] argsSplit = command.splitRemaining(" "); + if (argsSplit.length != 2) { + debug(player, "Missing arguments!"); + return false; + } + return htmlShowBuff(player, buffer, npc, argsSplit[0], argsSplit[1]); + } else if (command.matchAndRemove("unique ", "u ")) { + return htmlShowUnique(player, buffer, npc, command.getRemaining()); + } + + return false; + } + + @Override + public boolean executeActionCommand(L2PcInstance player, L2Npc npc, CommandProcessor command) { + AbstractBuffer buffer = BufferServiceRepository.getInstance().getConfig().determineBuffer(npc, player); + if (buffer == null) { + player.sendMessage("No authorization!"); + return false; + } + + if (command.matchAndRemove("target ", "t ")) { + executeTargetCommand(player, buffer, command); + } else if (command.matchAndRemove("unique ", "u ")) { + executeUniqueCommand(player, buffer, command); + } + + return true; + } + + @Override + protected boolean isDebugEnabled() { + return Configuration.bufferService().getDebug(); + } + + public static BufferService getInstance() { + return SingletonHolder.INSTANCE; + } + + private static final class SingletonHolder { + protected static final BufferService INSTANCE = new BufferService(); + } +} diff --git a/src/main/java/com/l2jserver/datapack/custom/service/buffer/BufferServiceBypassHandler.java b/src/main/java/com/l2jserver/datapack/custom/service/buffer/BufferServiceBypassHandler.java new file mode 100644 index 0000000000..3532000e93 --- /dev/null +++ b/src/main/java/com/l2jserver/datapack/custom/service/buffer/BufferServiceBypassHandler.java @@ -0,0 +1,65 @@ +/* + * Copyright © 2004-2020 L2J DataPack + * + * This file is part of L2J DataPack. + * + * L2J DataPack is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * L2J DataPack is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +package com.l2jserver.datapack.custom.service.buffer; + +import com.l2jserver.gameserver.handler.IBypassHandler; +import com.l2jserver.gameserver.model.actor.L2Character; +import com.l2jserver.gameserver.model.actor.L2Npc; +import com.l2jserver.gameserver.model.actor.instance.L2PcInstance; + +/** + * Buffer Service Bypass handler. + * @author HorridoJoho + * @version 2.6.2.0 + */ +public class BufferServiceBypassHandler implements IBypassHandler { + + public static final String BYPASS = "BufferService"; + + private static final String[] BYPASS_LIST = new String[] { + BYPASS + }; + + private BufferServiceBypassHandler() { + // Do nothing. + } + + @Override + public boolean useBypass(String command, L2PcInstance activeChar, L2Character target) { + if ((target == null) || !target.isNpc()) { + return false; + } + + BufferService.getInstance().executeCommand(activeChar, (L2Npc) target, command.substring(BYPASS.length()).trim()); + return true; + } + + @Override + public String[] getBypassList() { + return BYPASS_LIST; + } + + public static BufferServiceBypassHandler getInstance() { + return SingletonHolder.INSTANCE; + } + + private static final class SingletonHolder { + protected static final BufferServiceBypassHandler INSTANCE = new BufferServiceBypassHandler(); + } +} diff --git a/src/main/java/com/l2jserver/datapack/custom/service/buffer/BufferServiceItemHandler.java b/src/main/java/com/l2jserver/datapack/custom/service/buffer/BufferServiceItemHandler.java new file mode 100644 index 0000000000..7f06674673 --- /dev/null +++ b/src/main/java/com/l2jserver/datapack/custom/service/buffer/BufferServiceItemHandler.java @@ -0,0 +1,54 @@ +/* + * Copyright © 2004-2020 L2J DataPack + * + * This file is part of L2J DataPack. + * + * L2J DataPack is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * L2J DataPack is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +package com.l2jserver.datapack.custom.service.buffer; + +import com.l2jserver.gameserver.handler.IItemHandler; +import com.l2jserver.gameserver.model.actor.L2Playable; +import com.l2jserver.gameserver.model.actor.instance.L2PcInstance; +import com.l2jserver.gameserver.model.items.instance.L2ItemInstance; + +/** + * Buffer Service item handler. + * @author HorridoJoho + * @version 2.6.2.0 + */ +public final class BufferServiceItemHandler implements IItemHandler { + + private BufferServiceItemHandler() { + // Do nothing. + } + + @Override + public boolean useItem(L2Playable playable, L2ItemInstance item, boolean forceUse) { + if (!playable.isPlayer()) { + return false; + } + + BufferService.getInstance().executeCommand((L2PcInstance) playable, null, null); + return true; + } + + public static BufferServiceItemHandler getInstance() { + return SingletonHolder.INSTANCE; + } + + private static final class SingletonHolder { + protected static final BufferServiceItemHandler INSTANCE = new BufferServiceItemHandler(); + } +} diff --git a/src/main/java/com/l2jserver/datapack/custom/service/buffer/BufferServiceRepository.java b/src/main/java/com/l2jserver/datapack/custom/service/buffer/BufferServiceRepository.java new file mode 100644 index 0000000000..36b050939a --- /dev/null +++ b/src/main/java/com/l2jserver/datapack/custom/service/buffer/BufferServiceRepository.java @@ -0,0 +1,260 @@ +/* + * Copyright © 2004-2020 L2J DataPack + * + * This file is part of L2J DataPack. + * + * L2J DataPack is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * L2J DataPack is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +package com.l2jserver.datapack.custom.service.buffer; + +import static java.sql.Statement.RETURN_GENERATED_KEYS; + +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.concurrent.ConcurrentHashMap; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.l2jserver.commons.database.ConnectionFactory; +import com.l2jserver.datapack.custom.service.base.util.htmltmpls.HTMLTemplatePlaceholder; +import com.l2jserver.datapack.custom.service.buffer.model.BufferConfig; +import com.l2jserver.datapack.custom.service.buffer.model.UniqueBufflist; +import com.l2jserver.datapack.custom.service.buffer.model.entity.BuffSkill; +import com.l2jserver.gameserver.config.Configuration; +import com.l2jserver.gameserver.model.actor.instance.L2PcInstance; + +/** + * Buffer Service Data. + * @author HorridoJoho + * @version 2.6.2.0 + */ +public final class BufferServiceRepository { + public enum BuffType { + BUFF, + SONG_DANCE + } + + private static final Logger LOG = LoggerFactory.getLogger(BufferServiceRepository.class); + + private static final String SELECT_UNIQUE_BUFF_LISTS = "SELECT ulist_id, ulist_char_id, ulist_name FROM custom_buffer_service_ulists ORDER BY ulist_char_id"; + + private static final String SELECT_UNIQUE_BUFF_LIST = "SELECT ulist_buff_ident FROM custom_buffer_service_ulist_buffs WHERE ulist_id=?"; + + private static final String INSERT_UNIQUE_BUFF_LIST = "INSERT INTO custom_buffer_service_ulists (ulist_char_id, ulist_name) VALUES(?, ?)"; + + private static final String DELETE_UNIQUE_BUFF_LIST = "DELETE FROM custom_buffer_service_ulists WHERE ulist_char_id=? AND ulist_id=?"; + + private static final String INSERT_BUFF_TO_UNIQUE_BUFF_LIST = "INSERT INTO custom_buffer_service_ulist_buffs VALUES(?, ?)"; + + private static final String DELETE_BUFF_FROM_BUFF_LIST = "DELETE FROM custom_buffer_service_ulist_buffs WHERE ulist_id=? AND ulist_buff_ident=?"; + + private final BufferConfig config; + + protected final ConcurrentHashMap<Integer, Map<Integer, UniqueBufflist>> uniqueBufflists = new ConcurrentHashMap<>(); + + private BufferServiceRepository() { + config = new BufferConfig(); + + loadUniqueBufflists(); + } + + private void loadUniqueBufflists() { + try (var con = ConnectionFactory.getInstance().getConnection()) { + try (var st = con.createStatement(); + var rs = st.executeQuery(SELECT_UNIQUE_BUFF_LISTS)) { + while (rs.next()) { + int charId = rs.getInt("ulist_char_id"); + int ulistId = rs.getInt("ulist_id"); + String ulistName = rs.getString("ulist_name"); + + Map<Integer, UniqueBufflist> ulists = getPlayersULists(charId); + ulists.put(ulistId, new UniqueBufflist(ulistId, ulistName)); + } + } + + for (var ulists : uniqueBufflists.entrySet()) { + for (var ulist : ulists.getValue().entrySet()) { + try (var ps = con.prepareStatement(SELECT_UNIQUE_BUFF_LIST)) { + ps.setInt(1, ulist.getKey()); + try (var rs = ps.executeQuery()) { + while (rs.next()) { + String buffIdent = rs.getString("ulist_buff_ident"); + BuffSkill buff = config.getGlobal().getBuff(buffIdent); + if (buff == null) { + LOG.warn("BufferService - Data: Buff with ident does not exists!"); + } else { + ulist.getValue().add(buff); + } + } + } + } + } + } + } catch (Exception ex) { + LOG.error("Error loading unique buffs!", ex); + } + } + + private Map<Integer, UniqueBufflist> getPlayersULists(int playerObjectId) { + return uniqueBufflists.computeIfAbsent(playerObjectId, k -> new LinkedHashMap<>()); + } + + private UniqueBufflist getPlayersUList(int playerObjectId, String ulistName) { + Map<Integer, UniqueBufflist> ulists = getPlayersULists(playerObjectId); + for (Entry<Integer, UniqueBufflist> entry : ulists.entrySet()) { + if (entry.getValue().ulistName.equals(ulistName)) { + return entry.getValue(); + } + } + return null; + } + + public boolean createUniqueBufflist(int playerObjectId, String ulistName) { + // prevent duplicate entry + if (getPlayersUList(playerObjectId, ulistName) != null) { + return false; + } + + try (var con = ConnectionFactory.getInstance().getConnection(); + var ps = con.prepareStatement(INSERT_UNIQUE_BUFF_LIST, RETURN_GENERATED_KEYS)) { + ps.setInt(1, playerObjectId); + ps.setString(2, ulistName); + ps.executeUpdate(); + try (var rs = ps.getGeneratedKeys()) { + if (rs.next()) { + int newId = rs.getInt(1); + getPlayersULists(playerObjectId).put(newId, new UniqueBufflist(newId, ulistName)); + } + return true; + } + } catch (Exception ex) { + LOG.warn("Failed to insert unique bufflist!", ex); + return false; + } + } + + public void deleteUniqueBufflist(int playerObjectId, String ulistName) { + UniqueBufflist ulist = getPlayersUList(playerObjectId, ulistName); + if (ulist == null) { + return; + } + + try (var con = ConnectionFactory.getInstance().getConnection(); + var ps = con.prepareStatement(DELETE_UNIQUE_BUFF_LIST)) { + ps.setInt(1, playerObjectId); + ps.setInt(2, ulist.ulistId); + ps.executeUpdate(); + getPlayersULists(playerObjectId).remove(ulist.ulistId); + } catch (Exception ex) { + LOG.warn("Failed to delete unique bufflist!", ex); + } + } + + public boolean addToUniqueBufflist(L2PcInstance player, String ulistName, BuffSkill buff) { + UniqueBufflist ulist = getPlayersUList(player.getObjectId(), ulistName); + // prevent duplicate entry with ulist.contains(buff) + if ((ulist == null) || ulist.contains(buff) || ((buff.getType() == BuffType.BUFF) && (ulist.numBuffs >= player.getStat().getMaxBuffCount())) || ((buff.getType() == BuffType.SONG_DANCE) && (ulist.numSongsDances >= Configuration.character().getMaxDanceAmount()))) { + return false; + } + + if (addToUniqueBufflist(player.getObjectId(), ulist.ulistId, buff.getId())) { + ulist.add(buff); + return true; + } + return false; + } + + private boolean addToUniqueBufflist(int playerObjectId, int ulistId, String buffId) { + try (var con = ConnectionFactory.getInstance().getConnection(); + var ps = con.prepareStatement(INSERT_BUFF_TO_UNIQUE_BUFF_LIST)) { + ps.setInt(1, ulistId); + ps.setString(2, buffId); + ps.executeUpdate(); + } catch (Exception ex) { + LOG.warn("Failed to insert buff into unique bufflist!", ex); + return false; + } + return true; + } + + public void removeFromUniqueBufflist(int playerObjectId, String ulistName, BuffSkill buff) { + UniqueBufflist ulist = getPlayersUList(playerObjectId, ulistName); + if ((ulist == null) || !ulist.contains(buff)) { + return; + } + + try (var con = ConnectionFactory.getInstance().getConnection(); + var ps = con.prepareStatement(DELETE_BUFF_FROM_BUFF_LIST)) { + ps.setInt(1, ulist.ulistId); + ps.setString(2, buff.getId()); + ps.executeUpdate(); + ulist.remove(buff); + } catch (Exception ex) { + LOG.warn("Failed to remove buff from unique bufflist!", ex); + } + } + + public BufferConfig getConfig() { + return config; + } + + public boolean canHaveMoreBufflists(L2PcInstance player) { + return getPlayersULists(player.getObjectId()).size() < Configuration.bufferService().getMaxUniqueLists(); + } + + public boolean hasUniqueBufflist(int playerObjectId, String ulistName) { + return getPlayersUList(playerObjectId, ulistName) != null; + } + + public List<BuffSkill> getUniqueBufflist(int playerObjectId, String ulistName) { + UniqueBufflist ulist = getPlayersUList(playerObjectId, ulistName); + if (ulist == null) { + return null; + } + return Collections.unmodifiableList(ulist); + } + + public HTMLTemplatePlaceholder getPlayersUListPlaceholder(int playerObjectId, String ulistName) { + UniqueBufflist ulist = getPlayersUList(playerObjectId, ulistName); + if (ulist == null) { + return null; + } + return ulist.placeholder; + } + + public HTMLTemplatePlaceholder getPlayersUListsPlaceholder(int playerObjectId) { + Map<Integer, UniqueBufflist> ulists = getPlayersULists(playerObjectId); + if (ulists.isEmpty()) { + return null; + } + + HTMLTemplatePlaceholder placeholder = new HTMLTemplatePlaceholder("uniques", null); + for (Entry<Integer, UniqueBufflist> entry : ulists.entrySet()) { + placeholder.addAliasChild(String.valueOf(placeholder.getChildrenSize()), entry.getValue().placeholder); + } + return placeholder; + } + + public static BufferServiceRepository getInstance() { + return SingletonHolder.INSTANCE; + } + + private static class SingletonHolder { + static final BufferServiceRepository INSTANCE = new BufferServiceRepository(); + } +} diff --git a/src/main/java/com/l2jserver/datapack/custom/service/buffer/BufferServiceVoicedCommandHandler.java b/src/main/java/com/l2jserver/datapack/custom/service/buffer/BufferServiceVoicedCommandHandler.java new file mode 100644 index 0000000000..714f06655c --- /dev/null +++ b/src/main/java/com/l2jserver/datapack/custom/service/buffer/BufferServiceVoicedCommandHandler.java @@ -0,0 +1,58 @@ +/* + * Copyright © 2004-2020 L2J DataPack + * + * This file is part of L2J DataPack. + * + * L2J DataPack is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * L2J DataPack is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +package com.l2jserver.datapack.custom.service.buffer; + +import com.l2jserver.gameserver.config.Configuration; +import com.l2jserver.gameserver.handler.IVoicedCommandHandler; +import com.l2jserver.gameserver.model.actor.instance.L2PcInstance; + +/** + * Buffer service voiced command handler. + * @author HorridoJoho + * @version 2.6.2.0 + */ +public final class BufferServiceVoicedCommandHandler implements IVoicedCommandHandler { + + private static final String[] COMMANDS = new String[] { + Configuration.bufferService().getVoicedCommand() + }; + + private BufferServiceVoicedCommandHandler() { + // Do nothing. + } + + @Override + public boolean useVoicedCommand(String command, L2PcInstance activeChar, String params) { + BufferService.getInstance().executeCommand(activeChar, null, params); + return true; + } + + @Override + public String[] getVoicedCommandList() { + return COMMANDS; + } + + public static BufferServiceVoicedCommandHandler getInstance() { + return SingletonHolder.INSTANCE; + } + + private static final class SingletonHolder { + protected static final BufferServiceVoicedCommandHandler INSTANCE = new BufferServiceVoicedCommandHandler(); + } +} diff --git a/src/main/java/com/l2jserver/datapack/custom/service/buffer/model/BufferConfig.java b/src/main/java/com/l2jserver/datapack/custom/service/buffer/model/BufferConfig.java new file mode 100644 index 0000000000..ddd87f1b28 --- /dev/null +++ b/src/main/java/com/l2jserver/datapack/custom/service/buffer/model/BufferConfig.java @@ -0,0 +1,116 @@ +/* + * Copyright © 2004-2020 L2J DataPack + * + * This file is part of L2J DataPack. + * + * L2J DataPack is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * L2J DataPack is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +package com.l2jserver.datapack.custom.service.buffer.model; + +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.HashMap; +import java.util.Map; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.gson.Gson; +import com.l2jserver.datapack.custom.service.buffer.BufferService; +import com.l2jserver.datapack.custom.service.buffer.model.entity.AbstractBuffer; +import com.l2jserver.datapack.custom.service.buffer.model.entity.NpcBuffer; +import com.l2jserver.datapack.custom.service.buffer.model.entity.VoicedBuffer; +import com.l2jserver.gameserver.config.Configuration; +import com.l2jserver.gameserver.model.actor.L2Npc; +import com.l2jserver.gameserver.model.actor.instance.L2PcInstance; + +/** + * Buffer configuration. + * @author HorridoJoho + * @version 2.6.2.0 + */ +public final class BufferConfig { + + private static final Logger LOG = LoggerFactory.getLogger(BufferConfig.class); + + private GlobalConfig global; + + private VoicedBuffer voiced; + + private Map<Integer, NpcBuffer> npcs; + + public BufferConfig() { + try { + final var gson = new Gson(); + + final var jsonPath = Paths.get(Configuration.server().getDatapackRoot().getAbsolutePath(), "data", BufferService.SCRIPT_PATH.toString(), "json"); + + global = gson.fromJson(Files.newBufferedReader(jsonPath.resolve("global.json")), GlobalConfig.class); + voiced = gson.fromJson(Files.newBufferedReader(jsonPath.resolve("voiced.json")), VoicedBuffer.class); + npcs = new HashMap<>(); + + final var npcsDir = Paths.get(jsonPath.toString(), "npcs"); + try (var dirStream = Files.newDirectoryStream(npcsDir, "*.json")) { + for (var entry : dirStream) { + if (!Files.isRegularFile(entry)) { + continue; + } + + final var npc = gson.fromJson(Files.newBufferedReader(entry), NpcBuffer.class); + npcs.put(npc.getId(), npc); + } + } + + global.afterDeserialize(this); + voiced.afterDeserialize(this); + for (var npc : npcs.values()) { + npc.afterDeserialize(this); + } + } catch (Exception ex) { + LOG.error("Error loading buffer configuration!", ex); + } + } + + public AbstractBuffer determineBuffer(L2Npc npc, L2PcInstance player) { + if (npc == null) { + if (!Configuration.bufferService().getVoicedEnable() || ((Configuration.bufferService().getVoicedRequiredItem() > 0) && (player.getInventory().getAllItemsByItemId(Configuration.bufferService().getVoicedRequiredItem()) == null))) { + return null; + } + return voiced; + } + return npcs.get(npc.getId()); + } + + public void registerNpcs(BufferService scriptInstance) { + for (var npc : npcs.values()) { + if (npc.getDirectFirstTalk()) { + scriptInstance.addFirstTalkId(npc.getId()); + } + scriptInstance.addStartNpc(npc.getId()); + scriptInstance.addTalkId(npc.getId()); + } + } + + public GlobalConfig getGlobal() { + return global; + } + + public final VoicedBuffer getVoiced() { + return voiced; + } + + public final Map<Integer, NpcBuffer> getNpcs() { + return npcs; + } +} \ No newline at end of file diff --git a/src/main/java/com/l2jserver/datapack/custom/service/buffer/model/GlobalConfig.java b/src/main/java/com/l2jserver/datapack/custom/service/buffer/model/GlobalConfig.java new file mode 100644 index 0000000000..9a9b6898c8 --- /dev/null +++ b/src/main/java/com/l2jserver/datapack/custom/service/buffer/model/GlobalConfig.java @@ -0,0 +1,56 @@ +/* + * Copyright © 2004-2020 L2J DataPack + * + * This file is part of L2J DataPack. + * + * L2J DataPack is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * L2J DataPack is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +package com.l2jserver.datapack.custom.service.buffer.model; + +import java.util.Map; + +import com.l2jserver.datapack.custom.service.buffer.model.entity.BuffCategory; +import com.l2jserver.datapack.custom.service.buffer.model.entity.BuffSkill; + +/** + * Global configuration. + * @author HorridoJoho + * @version 2.6.2.0 + */ +public final class GlobalConfig { + private Map<String, BuffSkill> buffs; + private Map<String, BuffCategory> buffCategories; + + public void afterDeserialize(BufferConfig config) { + for (var buff : buffs.values()) { + buff.afterDeserialize(config); + } + + for (var category : buffCategories.values()) { + category.afterDeserialize(config); + } + } + + public BuffSkill getBuff(String id) { + return buffs.get(id); + } + + public final Map<String, BuffSkill> getBuffs() { + return buffs; + } + + public final Map<String, BuffCategory> getCategories() { + return buffCategories; + } +} \ No newline at end of file diff --git a/src/main/java/com/l2jserver/datapack/custom/service/buffer/model/UniqueBufflist.java b/src/main/java/com/l2jserver/datapack/custom/service/buffer/model/UniqueBufflist.java new file mode 100644 index 0000000000..f5cb17cc8a --- /dev/null +++ b/src/main/java/com/l2jserver/datapack/custom/service/buffer/model/UniqueBufflist.java @@ -0,0 +1,87 @@ +/* + * Copyright © 2004-2020 L2J DataPack + * + * This file is part of L2J DataPack. + * + * L2J DataPack is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * L2J DataPack is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +package com.l2jserver.datapack.custom.service.buffer.model; + +import java.util.LinkedList; + +import com.l2jserver.datapack.custom.service.base.util.htmltmpls.HTMLTemplatePlaceholder; +import com.l2jserver.datapack.custom.service.buffer.BufferServiceRepository.BuffType; +import com.l2jserver.datapack.custom.service.buffer.model.entity.BuffSkill; + +/** + * This class is here so we can actually get the name of this list and make placeholder adjustments easily while keeping outside code cleaner + * @author HorridoJoho + * @version 2.6.2.0 + */ +public class UniqueBufflist extends LinkedList<BuffSkill> { + private static final long serialVersionUID = -2586607798277226501L; + + public final int ulistId; + public final String ulistName; + public int numBuffs; + public int numSongsDances; + public HTMLTemplatePlaceholder placeholder; + + public UniqueBufflist(int ulistId, String ulistName) { + this.ulistId = ulistId; + this.ulistName = ulistName; + this.numBuffs = 0; + this.numSongsDances = 0; + this.placeholder = new HTMLTemplatePlaceholder("unique", null).addChild("buffs", null).addChild("name", ulistName).addChild("num_buffs", "0").addChild("num_songs_dances", "0"); + } + + @Override + public boolean add(BuffSkill e) { + if (super.add(e)) { + if (e.getType() == BuffType.BUFF) { + ++this.numBuffs; + this.placeholder.getChild("num_buffs").setValue(String.valueOf(Integer.parseInt(this.placeholder.getChild("num_buffs").getValue()) + 1)); + } else { + ++this.numSongsDances; + this.placeholder.getChild("num_songs_dances").setValue(String.valueOf(Integer.parseInt(this.placeholder.getChild("num_songs_dances").getValue()) + 1)); + } + this.placeholder.getChild("buffs").addAliasChild(e.getId(), e.getPlaceholder()); + return true; + } + + return false; + } + + @Override + public boolean remove(Object o) { + if (super.remove(o)) { + switch (((BuffSkill) o).getType()) { + case BUFF: + --numBuffs; + break; + case SONG_DANCE: + --numSongsDances; + break; + } + + this.placeholder = new HTMLTemplatePlaceholder("unique", null).addChild("buffs", null).addChild("name", this.ulistName).addChild("num_buffs", String.valueOf(numBuffs)).addChild("num_songs_dances", String.valueOf(numSongsDances)); + for (BuffSkill buff : this) { + this.placeholder.getChild("buffs").addAliasChild(buff.getId(), buff.getPlaceholder()); + } + return true; + } + + return false; + } +} diff --git a/src/main/java/com/l2jserver/datapack/custom/service/buffer/model/entity/AbstractBuffer.java b/src/main/java/com/l2jserver/datapack/custom/service/buffer/model/entity/AbstractBuffer.java new file mode 100644 index 0000000000..2aa0461dc6 --- /dev/null +++ b/src/main/java/com/l2jserver/datapack/custom/service/buffer/model/entity/AbstractBuffer.java @@ -0,0 +1,97 @@ +/* + * Copyright © 2004-2020 L2J DataPack + * + * This file is part of L2J DataPack. + * + * L2J DataPack is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * L2J DataPack is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +package com.l2jserver.datapack.custom.service.buffer.model.entity; + +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import com.l2jserver.datapack.custom.service.base.model.entity.CustomServiceServer; +import com.l2jserver.datapack.custom.service.base.util.htmltmpls.HTMLTemplatePlaceholder; +import com.l2jserver.datapack.custom.service.buffer.model.BufferConfig; +import com.l2jserver.gameserver.config.Configuration; + +/** + * Abstract buffer. + * @author HorridoJoho + * @version 2.6.2.0 + */ +public abstract class AbstractBuffer extends CustomServiceServer { + private boolean canHeal; + private boolean canCancel; + private List<String> presetBuffCategories; + private List<String> buffCategories; + + private final transient Map<String, BuffCategory> presetBuffCatsMap = new LinkedHashMap<>(); + private final transient Map<String, BuffCategory> buffCatsMap = new LinkedHashMap<>(); + + public AbstractBuffer(String bypassPrefix) { + super(bypassPrefix, "buffer"); + } + + public void afterDeserialize(BufferConfig config) { + super.afterDeserialize(); + + for (String id : presetBuffCategories) { + presetBuffCatsMap.put(id, config.getGlobal().getCategories().get(id)); + } + + for (String id : buffCategories) { + buffCatsMap.put(id, config.getGlobal().getCategories().get(id)); + } + + if (canHeal) { + getPlaceholder().addChild("can_heal", null); + } + if (canCancel) { + getPlaceholder().addChild("can_cancel", null); + } + if (!presetBuffCategories.isEmpty()) { + HTMLTemplatePlaceholder presetBufflistsPlaceholder = getPlaceholder().addChild("presets", null).getChild("presets"); + for (Entry<String, BuffCategory> presetBufflist : presetBuffCatsMap.entrySet()) { + presetBufflistsPlaceholder.addAliasChild(String.valueOf(presetBufflistsPlaceholder.getChildrenSize()), presetBufflist.getValue().getPlaceholder()); + } + } + if (!buffCategories.isEmpty()) { + HTMLTemplatePlaceholder buffCatsPlaceholder = getPlaceholder().addChild("categories", null).getChild("categories"); + for (Entry<String, BuffCategory> buffCat : buffCatsMap.entrySet()) { + buffCatsPlaceholder.addAliasChild(String.valueOf(buffCatsPlaceholder.getChildrenSize()), buffCat.getValue().getPlaceholder()); + } + } + + getPlaceholder().addChild("max_unique_lists", String.valueOf(Configuration.bufferService().getMaxUniqueLists())); + } + + public final boolean getCanHeal() { + return canHeal; + } + + public final boolean getCanCancel() { + return canCancel; + } + + public Map<String, BuffCategory> getPresetBuffCats() { + return presetBuffCatsMap; + } + + public final Map<String, BuffCategory> getBuffCats() { + return buffCatsMap; + } +} diff --git a/src/main/java/com/l2jserver/datapack/custom/service/buffer/model/entity/BuffCategory.java b/src/main/java/com/l2jserver/datapack/custom/service/buffer/model/entity/BuffCategory.java new file mode 100644 index 0000000000..935658b5e1 --- /dev/null +++ b/src/main/java/com/l2jserver/datapack/custom/service/buffer/model/entity/BuffCategory.java @@ -0,0 +1,68 @@ +/* + * Copyright © 2004-2020 L2J DataPack + * + * This file is part of L2J DataPack. + * + * L2J DataPack is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * L2J DataPack is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +package com.l2jserver.datapack.custom.service.buffer.model.entity; + +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import com.l2jserver.datapack.custom.service.base.model.entity.Refable; +import com.l2jserver.datapack.custom.service.base.util.htmltmpls.HTMLTemplatePlaceholder; +import com.l2jserver.datapack.custom.service.buffer.model.BufferConfig; + +/** + * Buff category. + * @author HorridoJoho + * @version 2.6.2.0 + */ +public class BuffCategory extends Refable { + private String name; + private List<String> buffs; + + private final transient Map<String, BuffSkill> buffSkillsMap = new LinkedHashMap<>(); + + public void afterDeserialize(BufferConfig config) { + super.afterDeserialize(); + + for (String id : buffs) { + buffSkillsMap.put(id, config.getGlobal().getBuff(id)); + } + + getPlaceholder().addChild("name", name); + if (!buffs.isEmpty()) { + HTMLTemplatePlaceholder buffsPlaceholder = getPlaceholder().addChild("buffs", null).getChild("buffs"); + for (Entry<String, BuffSkill> buff : buffSkillsMap.entrySet()) { + buffsPlaceholder.addAliasChild(String.valueOf(buffsPlaceholder.getChildrenSize()), buff.getValue().getPlaceholder()); + } + } + } + + public String getName() { + return name; + } + + public Map<String, BuffSkill> getBuffs() { + return buffSkillsMap; + } + + public BuffSkill getBuff(String id) { + return buffSkillsMap.get(id); + } +} diff --git a/src/main/java/com/l2jserver/datapack/custom/service/buffer/model/entity/BuffSkill.java b/src/main/java/com/l2jserver/datapack/custom/service/buffer/model/entity/BuffSkill.java new file mode 100644 index 0000000000..c26c60317c --- /dev/null +++ b/src/main/java/com/l2jserver/datapack/custom/service/buffer/model/entity/BuffSkill.java @@ -0,0 +1,83 @@ +/* + * Copyright © 2004-2020 L2J DataPack + * + * This file is part of L2J DataPack. + * + * L2J DataPack is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * L2J DataPack is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +package com.l2jserver.datapack.custom.service.buffer.model.entity; + +import com.l2jserver.datapack.custom.service.base.model.entity.CustomServiceProduct; +import com.l2jserver.datapack.custom.service.buffer.BufferServiceRepository.BuffType; +import com.l2jserver.datapack.custom.service.buffer.model.BufferConfig; +import com.l2jserver.gameserver.datatables.SkillData; +import com.l2jserver.gameserver.model.skills.Skill; + +/** + * Buff skill. + * @author HorridoJoho + * @version 2.6.2.0 + */ +public class BuffSkill extends CustomServiceProduct { + private int skill; + private int level; + private BuffType type; + + public void afterDeserialize(BufferConfig config) { + super.afterDeserialize(); + + final Skill skill = getSkill(); + getPlaceholder().addChild("skill_id", String.valueOf(skill.getId())).addChild("skill_name", skill.getName()).addChild("skill_icon", skill.getIcon()).addChild("type", type.toString()); + } + + public int getSkillId() { + return skill; + } + + public int getSkillLevel() { + return level; + } + + public BuffType getType() { + return type; + } + + public Skill getSkill() { + return SkillData.getInstance().getSkill(skill, level); + } + + @SuppressWarnings("unused") + private String getClientSkillIconSource(int skillId) { + String format; + if (skillId < 100) { + format = "00" + skillId; + } else if (skillId < 1000) { + format = "0" + skillId; + } else if (skillId == 1517) { + format = "1499"; + } else if (skillId == 1518) { + format = "1502"; + } else { + if ((skillId > 4698) && (skillId < 4701)) { + format = "1331"; + } else if ((skillId > 4701) && (skillId < 4704)) { + format = "1332"; + } else { + format = Integer.toString(skillId); + } + } + + return "icon.skill" + format; + } +} diff --git a/src/main/java/com/l2jserver/datapack/custom/service/buffer/model/entity/NpcBuffer.java b/src/main/java/com/l2jserver/datapack/custom/service/buffer/model/entity/NpcBuffer.java new file mode 100644 index 0000000000..60c5687720 --- /dev/null +++ b/src/main/java/com/l2jserver/datapack/custom/service/buffer/model/entity/NpcBuffer.java @@ -0,0 +1,64 @@ +/* + * Copyright © 2004-2020 L2J DataPack + * + * This file is part of L2J DataPack. + * + * L2J DataPack is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * L2J DataPack is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +package com.l2jserver.datapack.custom.service.buffer.model.entity; + +import com.l2jserver.datapack.custom.service.base.model.entity.IRefable; +import com.l2jserver.datapack.custom.service.buffer.BufferServiceBypassHandler; +import com.l2jserver.datapack.custom.service.buffer.model.BufferConfig; +import com.l2jserver.gameserver.data.xml.impl.NpcData; +import com.l2jserver.gameserver.model.actor.templates.L2NpcTemplate; + +/** + * NPC buffer. + * @author HorridoJoho + * @version 2.6.2.0 + */ +public final class NpcBuffer extends AbstractBuffer implements IRefable<Integer> { + private Integer npcId; + private boolean directFirstTalk; + + public NpcBuffer() { + super(BufferServiceBypassHandler.BYPASS); + } + + @Override + public void afterDeserialize(BufferConfig config) { + super.afterDeserialize(config); + + getPlaceholder().addChild("ident", String.valueOf(npcId)); + } + + public L2NpcTemplate getNpc() { + return NpcData.getInstance().getTemplate(npcId); + } + + @Override + public String getName() { + return getNpc().getName(); + } + + @Override + public Integer getId() { + return npcId; + } + + public boolean getDirectFirstTalk() { + return directFirstTalk; + } +} diff --git a/src/main/java/com/l2jserver/datapack/custom/service/buffer/model/entity/VoicedBuffer.java b/src/main/java/com/l2jserver/datapack/custom/service/buffer/model/entity/VoicedBuffer.java new file mode 100644 index 0000000000..f3e79cb9ef --- /dev/null +++ b/src/main/java/com/l2jserver/datapack/custom/service/buffer/model/entity/VoicedBuffer.java @@ -0,0 +1,49 @@ +/* + * Copyright © 2004-2020 L2J DataPack + * + * This file is part of L2J DataPack. + * + * L2J DataPack is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * L2J DataPack is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +package com.l2jserver.datapack.custom.service.buffer.model.entity; + +import com.l2jserver.datapack.custom.service.buffer.model.BufferConfig; +import com.l2jserver.gameserver.config.Configuration; +import com.l2jserver.gameserver.datatables.ItemTable; +import com.l2jserver.gameserver.model.items.L2Item; + +/** + * Voiced buffer. + * @author HorridoJoho + * @version 2.6.2.0 + */ +public final class VoicedBuffer extends AbstractBuffer { + public VoicedBuffer() { + super("voice ." + Configuration.bufferService().getVoicedCommand()); + } + + @Override + public void afterDeserialize(BufferConfig config) { + super.afterDeserialize(config); + } + + public L2Item getRequiredItem() { + return ItemTable.getInstance().getTemplate(Configuration.bufferService().getVoicedRequiredItem()); + } + + @Override + public String getName() { + return Configuration.bufferService().getVoicedName(); + } +} diff --git a/src/main/java/com/l2jserver/datapack/quests/Q00325_GrimCollector/Q00325_GrimCollector.java b/src/main/java/com/l2jserver/datapack/quests/Q00325_GrimCollector/Q00325_GrimCollector.java index afc65d3b41..7a6fdbc8c2 100644 --- a/src/main/java/com/l2jserver/datapack/quests/Q00325_GrimCollector/Q00325_GrimCollector.java +++ b/src/main/java/com/l2jserver/datapack/quests/Q00325_GrimCollector/Q00325_GrimCollector.java @@ -129,20 +129,20 @@ public final class Q00325_GrimCollector extends Quest { final long totalCount = (head + heart + liver + skull + rib + spine + arm + thigh + complete); if (totalCount > 0) { long sum = ((head * 30) + (heart * 20) + (liver * 20) + (skull * 100) + (rib * 40) + (spine * 14) + (arm * 14) + (thigh * 14)); - + if (totalCount >= 10) { sum += 1629; } - + if (complete > 0) { sum += 543 + (complete * 341); } - + st.giveAdena(sum, true); } - + takeItems(player, -1, ZOMBIE_HEAD, ZOMBIE_HEART, ZOMBIE_LIVER, SKULL, RIB_BONE, SPINE, ARM_BONE, THIGH_BONE, COMPLETE_SKELETON); - + if (event.equals("30434-06.html")) { st.exitQuest(true, true); } diff --git a/src/main/resources/data/scripts.cfg b/src/main/resources/data/scripts.cfg index 3ae6d83b77..ed637af580 100644 --- a/src/main/resources/data/scripts.cfg +++ b/src/main/resources/data/scripts.cfg @@ -8,6 +8,7 @@ com/l2jserver/datapack/features/SkillTransfer/SkillTransfer.java # Custom com/l2jserver/datapack/custom/Validators/SubClassSkills.java +com/l2jserver/datapack/custom/service/buffer/BufferService.java # Custom Events com/l2jserver/datapack/custom/events/Elpies/Elpies.java diff --git a/src/main/resources/data/service/buffer/html/community/category.html b/src/main/resources/data/service/buffer/html/community/category.html new file mode 100644 index 0000000000..f8d6f8785c --- /dev/null +++ b/src/main/resources/data/service/buffer/html/community/category.html @@ -0,0 +1,27 @@ +<html><title>%buffer.name% - Buffer</title><body><center> + +[INC(data/service/buffer/html/community/inc/header.html)ENDINC] + +<table width=570> + <tr> + <td><center> + <table width=200 cellspacing=0 cellpadding=0> + <tr><td width=32 height=32><button width=32 height=23 fore=L2UI_CT1.Button_DF back=L2UI_CT1.Button_DF_Down value="<<" action="%buffer.bypass_prefix% h m"></td><td width=200><center><font line="UNDERLINE" name="hs12" color=LEVEL>%category.name%</font></center></td></tr> + [FOREACH(buff IN category.buffs DO + [EXISTS(active_unique, + [IFCHILDS(active_unique.buffs, skill_id != buff.skill_id THEN + <tr><td height=35><img src="%buff.skill_icon%" width=32 height=32></td><td><center><button width=162 height=32 fore=L2UI_CT1.Button_DF back=L2UI_CT1.Button_DF_Down value="%buff.skill_name%" action="%buffer.bypass_prefix% u a %active_unique.name% %category.ident% %buff.ident%"></center></td></tr> + )ENDIFCHILDS] + )ENDEXISTS] + [EXISTS(!active_unique, + <tr><td height=35><img src="%buff.skill_icon%" width=32 height=32></td><td><center><button width=162 height=32 fore=L2UI_CT1.Button_DF back=L2UI_CT1.Button_DF_Down value="%buff.skill_name%" action="%buffer.bypass_prefix% t p b %category.ident% %buff.ident%"></center></td></tr> + )ENDEXISTS] + )ENDEACH] + <tr><td height=32></td><td></td></tr> + </table> + </center></td> + [INC(data/service/buffer/html/community/inc/active_unique_table.html)ENDINC] + </tr> +</table> + +</center></body></html> \ No newline at end of file diff --git a/src/main/resources/data/service/buffer/html/community/inc/active_unique_table.html b/src/main/resources/data/service/buffer/html/community/inc/active_unique_table.html new file mode 100644 index 0000000000..7cc3f585e1 --- /dev/null +++ b/src/main/resources/data/service/buffer/html/community/inc/active_unique_table.html @@ -0,0 +1,10 @@ +[EXISTS(active_unique, +<td><center> + <table width=200 cellspacing=0 cellpadding=0> + <tr><td width=168><center><font line="UNDERLINE" name="hs12" color=LEVEL>%active_unique.name% (%active_unique.num_buffs%/%player.unique_max_buffs%, %active_unique.num_songs_dances%/%player.unique_max_songs_dances%)</font></center></td><td height=32><button width=32 height=23 fore=L2UI_CT1.Button_DF back=L2UI_CT1.Button_DF_Down value="Des." action="%buffer.bypass_prefix% u des"></td></tr> + [FOREACH(buff IN active_unique.buffs DO + <tr><td><center><button width=163 height=32 fore=L2UI_CT1.Button_DF back=L2UI_CT1.Button_DF_Down value="%buff.skill_name%" action="%buffer.bypass_prefix% u r %active_unique.name% %buff.ident%"></center></td><td height=35><img src="%buff.skill_icon%" width=32 height=32></td></tr> + )ENDEACH] + </table> +</center></td> +)ENDEXISTS] diff --git a/src/main/resources/data/service/buffer/html/community/inc/header.html b/src/main/resources/data/service/buffer/html/community/inc/header.html new file mode 100644 index 0000000000..17daeb3d9e --- /dev/null +++ b/src/main/resources/data/service/buffer/html/community/inc/header.html @@ -0,0 +1,36 @@ +<table width=650 cellpadding=0 cellspacing=0> + <tr> + <td><combobox width=160 var="header_unique_selection" list="[FOREACH(unique IN uniques DO %unique.name%;)ENDEACH]" [EXISTS(active_unique, sel="%active_unique.name%")ENDEXISTS]></td> + <td><center><button width=100 height=23 fore=L2UI_CT1.Button_DF back=L2UI_CT1.Button_DF_Down value="Buff Me" action="%buffer.bypass_prefix% t p u $header_unique_selection"></center></td> + <td><center><button width=100 height=23 fore=L2UI_CT1.Button_DF back=L2UI_CT1.Button_DF_Down value="Buff Summon" action="%buffer.bypass_prefix% t s u $header_unique_selection"></center></td> + <td width=30></td> + <td><center><button width=115 height=23 fore=L2UI_CT1.Button_DF back=L2UI_CT1.Button_DF_Down value="Heal Me" action="%buffer.bypass_prefix% t p h"></center></td> + <td><center><button width=115 height=23 fore=L2UI_CT1.Button_DF back=L2UI_CT1.Button_DF_Down value="Heal Summon" action="%buffer.bypass_prefix% t s h"></center></td> + </tr> + <tr> + <td></td> + <td><center><button width=100 height=23 fore=L2UI_CT1.Button_DF back=L2UI_CT1.Button_DF_Down value="Select" action="%buffer.bypass_prefix% u s $header_unique_selection"></center></td> + <td><center><button width=100 height=23 fore=L2UI_CT1.Button_DF back=L2UI_CT1.Button_DF_Down value="Delete" action="%buffer.bypass_prefix% u del $header_unique_selection"></center></td> + <td></td> + <td><center><button width=115 height=23 fore=L2UI_CT1.Button_DF back=L2UI_CT1.Button_DF_Down value="Cancel Me" action="%buffer.bypass_prefix% t p c"></center></td> + <td><center><button width=115 height=23 fore=L2UI_CT1.Button_DF back=L2UI_CT1.Button_DF_Down value="Cancel Summon" action="%buffer.bypass_prefix% t s c"></center></td> + </tr> + <tr> + <td height=15></td><td></td><td></td><td></td><td></td><td></td> + </tr> + <tr> + <td><edit width=160 var="unique_new"></td> + <td><center><button width=100 height=23 fore="L2UI_CT1.Button_DF" back="L2UI_CT1.Button_DF_Down" value="Create Empty" action="%buffer.bypass_prefix% u c $unique_new"></center></td> + <td><center><button width=100 height=23 fore="L2UI_CT1.Button_DF" back="L2UI_CT1.Button_DF_Down" value="From Buffs" action="%buffer.bypass_prefix% u cfe $unique_new"></center></td> + <td></td> + <td></td> + <td></td> + </tr> +</table><br><br> +<font line="UNDERLINE" name="hs12" color=LEVEL>Presets</font><br1> +<table width=400 cellspacing=0 cellpadding=0> + <tr> + <td><center><button width=195 height=23 fore="L2UI_CT1.Button_DF" back="L2UI_CT1.Button_DF_Down" value="Fighter" action="%buffer.bypass_prefix% t p p BC_FIGHTER"></center></td> + <td><center><button width=195 height=23 fore="L2UI_CT1.Button_DF" back="L2UI_CT1.Button_DF_Down" value="Mage" action="%buffer.bypass_prefix% t p p BC_MAGE"></center></td> + </tr> +</table><br> \ No newline at end of file diff --git a/src/main/resources/data/service/buffer/html/community/main.html b/src/main/resources/data/service/buffer/html/community/main.html new file mode 100644 index 0000000000..34aad84a9b --- /dev/null +++ b/src/main/resources/data/service/buffer/html/community/main.html @@ -0,0 +1,20 @@ +<html><title>%buffer.name% - Buffer</title><body><center> + +[INC(data/service/buffer/html/community/inc/header.html)ENDINC] + +<table width=570> + <tr> + <td><center> + <table width=200 cellpadding=0 cellspacing=0> + <tr><td><center><font line="UNDERLINE" name="hs12" color=LEVEL>Categories</font></center></td></tr> + <tr><td height=15></td></tr> + [FOREACH(category IN buffer.categories DO + <tr><td><center><button width=195 height=23 fore=L2UI_CT1.Button_DF back=L2UI_CT1.Button_DF_Down value="%category.name%" action="%buffer.bypass_prefix% h c %category.ident%"></center></td></tr> + )ENDEACH] + </table> + </center></td> + [INC(data/service/buffer/html/community/inc/active_unique_table.html)ENDINC] + </tr> +</table> + +</center></body></html> \ No newline at end of file diff --git a/src/main/resources/data/service/buffer/html/npc/category.html b/src/main/resources/data/service/buffer/html/npc/category.html new file mode 100644 index 0000000000..a9908ac1e1 --- /dev/null +++ b/src/main/resources/data/service/buffer/html/npc/category.html @@ -0,0 +1,42 @@ +<html><title>%buffer.name% - Buffer</title><body> + +<a action="%buffer.bypass_prefix%">Home</a> > Category > <font color=LEVEL>%category.name%</font><br><center> + +[EXISTS(!active_unique, + <table width=270 cellspacing=0 cellpadding=0> + <tr> + <td><center><combobox width=130 var="unique_selection" list="[FOREACH(unique IN uniques DO %unique.name%;)ENDEACH]"></center></td> + <td><center><button width=130 height=23 fore=L2UI_CT1.Button_DF back=L2UI_CT1.Button_DF_Down value="Select" action="%buffer.bypass_prefix% u s $unique_selection"></center></td> + </tr> + </table><br> +)ENDEXISTS] +[EXISTS(active_unique, + <table width=270 cellspacing=0 cellpadding=0> + <tr> + <td width=135><center><font color=LEVEL>%active_unique.name%</font> (%active_unique.num_buffs%/%player.unique_max_buffs%, %active_unique.num_songs_dances%/%player.unique_max_songs_dances%)</font></center></td> + <td><center><button width=130 height=23 fore=L2UI_CT1.Button_DF back=L2UI_CT1.Button_DF_Down value="Deselect" action="%buffer.bypass_prefix% u des"></center></center></td> + </tr> + </table><br> +)ENDEXISTS] + +<table width=270 cellspacing=0 cellpadding=0> + [FOREACH(buff IN category.buffs DO + [EXISTS(active_unique, + [IFCHILDS(active_unique.buffs, skill_id != buff.skill_id THEN + <tr> + <td width=32 height=32><img width=32 height=32 src="%buff.skill_icon%"></td> + <td><button width=238 height=32 fore=L2UI_CT1.Button_DF back=L2UI_CT1.Button_DF_Down value="%buff.skill_name%" action="%buffer.bypass_prefix% u a %active_unique.name% %category.ident% %buff.ident%"></td> + </tr> + <tr><td height=10></td><td></td></tr> + )ENDIFCHILDS] + )ENDEXISTS] + [EXISTS(!active_unique, + <tr> + <td width=32 height=32><img width=32 height=32 src="%buff.skill_icon%"></td> + <td><button width=238 height=32 fore=L2UI_CT1.Button_DF back=L2UI_CT1.Button_DF_Down value="%buff.skill_name%" action="%buffer.bypass_prefix% t p b %category.ident% %buff.ident%"></td> + </tr> + <tr><td height=10></td><td></td></tr> + )ENDEXISTS] + )ENDEACH] +</table> +</center></body></html> \ No newline at end of file diff --git a/src/main/resources/data/service/buffer/html/npc/main.html b/src/main/resources/data/service/buffer/html/npc/main.html new file mode 100644 index 0000000000..f229dfe1b6 --- /dev/null +++ b/src/main/resources/data/service/buffer/html/npc/main.html @@ -0,0 +1,67 @@ +<html><title>%buffer.name% - Buffer</title><body><center> + +<table width=270 cellspacing=0 cellpadding=0> + <tr> + <td><center><button width=130 height=23 fore=L2UI_CT1.Button_DF back=L2UI_CT1.Button_DF_Down value="Heal Me" action="%buffer.bypass_prefix% t p h"></center></td> + <td><center><button width=130 height=23 fore=L2UI_CT1.Button_DF back=L2UI_CT1.Button_DF_Down value="Heal Summon" action="%buffer.bypass_prefix% t s h"></center></td> + </tr> +</table> + +<table width=270 cellspacing=0 cellpadding=0> + <tr> + <td><center><button width=130 height=23 fore=L2UI_CT1.Button_DF back=L2UI_CT1.Button_DF_Down value="Cancel Me" action="%buffer.bypass_prefix% t p c"></center></td> + <td><center><button width=130 height=23 fore=L2UI_CT1.Button_DF back=L2UI_CT1.Button_DF_Down value="Cancel Summon" action="%buffer.bypass_prefix% t s c"></center></td> + </tr> +</table><br> + +<font name=hs12 line=UNDERLINE color=LEVEL>Presets</font><br1> +<table width=270 cellspacing=0 cellpadding=0> + <tr> + <td><center><button width=130 height=23 fore=L2UI_CT1.Button_DF back=L2UI_CT1.Button_DF_Down value="Fighter" action="%buffer.bypass_prefix% t p p BC_FIGHTER"></center></td> + <td><center><button width=130 height=23 fore=L2UI_CT1.Button_DF back=L2UI_CT1.Button_DF_Down value="Mage" action="%buffer.bypass_prefix% t p p BC_MAGE"></center></td> + </tr> +</table><br> + +<font name=hs12 line=UNDERLINE color=LEVEL>Categories</font><br1> +<table width=270 cellspacing=0 cellpadding=0> + <tr> + <td><center><button width=130 height=23 fore=L2UI_CT1.Button_DF back=L2UI_CT1.Button_DF_Down value="Buffs" action="%buffer.bypass_prefix% h c BC_BUFFS"></center></td> + <td><center><button width=130 height=23 fore=L2UI_CT1.Button_DF back=L2UI_CT1.Button_DF_Down value="Songs" action="%buffer.bypass_prefix% h c BC_SONGS"></center></td> + </tr> + <tr> + <td><center><button width=130 height=23 fore=L2UI_CT1.Button_DF back=L2UI_CT1.Button_DF_Down value="Dances" action="%buffer.bypass_prefix% h c BC_DANCES"></center></td> + <td><center><button width=130 height=23 fore=L2UI_CT1.Button_DF back=L2UI_CT1.Button_DF_Down value="Chants" action="%buffer.bypass_prefix% h c BC_CHANTS"></center></td> + </tr> + <tr> + <td><center><button width=130 height=23 fore=L2UI_CT1.Button_DF back=L2UI_CT1.Button_DF_Down value="Dwarven" action="%buffer.bypass_prefix% h c BC_DWARVEN"></center></td> + <td><center><button width=130 height=23 fore=L2UI_CT1.Button_DF back=L2UI_CT1.Button_DF_Down value="Resist" action="%buffer.bypass_prefix% h c BC_RESIST"></center></td> + </tr> + <tr> + <td><center><button width=130 height=23 fore=L2UI_CT1.Button_DF back=L2UI_CT1.Button_DF_Down value="Special" action="%buffer.bypass_prefix% h c BC_SPECIAL"></center></td> + <td><center><button width=130 height=23 fore=L2UI_CT1.Button_DF back=L2UI_CT1.Button_DF_Down value="Overlord" action="%buffer.bypass_prefix% h c BC_OVERLORD"></center></td> + </tr> +</table><br> + +<font name=hs12 line=UNDERLINE color=LEVEL>Unique</font><br1> +<table width=270 cellspacing=0 cellpadding=0> + <tr> + <td><center><combobox width=85 var="unique_selection" list="[FOREACH(unique IN uniques DO %unique.name%;)ENDEACH]"></center></td> + <td><center><button width=85 height=23 fore=L2UI_CT1.Button_DF back=L2UI_CT1.Button_DF_Down value="Buff Me" action="%buffer.bypass_prefix% t p u $unique_selection"></center></td> + <td><center><button width=85 height=23 fore=L2UI_CT1.Button_DF back=L2UI_CT1.Button_DF_Down value="Buff Summon" action="%buffer.bypass_prefix% t s u $unique_selection"></center></td> + </tr> + <tr> + <td></td> + <td><center><button width=85 height=23 fore=L2UI_CT1.Button_DF back=L2UI_CT1.Button_DF_Down value="Inspect" action="%buffer.bypass_prefix% h u $unique_selection"></center></td> + <td><center><button width=85 height=23 fore=L2UI_CT1.Button_DF back=L2UI_CT1.Button_DF_Down value="Delete" action="%buffer.bypass_prefix% u del $unique_selection"></center></td> + </tr> + <tr> + <td height=15></td><td></td><td></td> + </tr> + <tr> + <td><center><edit width=85 var="unique_new"></center></td> + <td><center><button width=85 height=23 fore=L2UI_CT1.Button_DF back=L2UI_CT1.Button_DF_Down value="Create Empty" action="%buffer.bypass_prefix% u c $unique_new"></center></td> + <td><center><button width=85 height=23 fore=L2UI_CT1.Button_DF back=L2UI_CT1.Button_DF_Down value="From Buffs" action="%buffer.bypass_prefix% u cfe $unique_new"></center></td> + </tr> +</table> + +</center></body></html> \ No newline at end of file diff --git a/src/main/resources/data/service/buffer/html/npc/unique.html b/src/main/resources/data/service/buffer/html/npc/unique.html new file mode 100644 index 0000000000..5f969b3189 --- /dev/null +++ b/src/main/resources/data/service/buffer/html/npc/unique.html @@ -0,0 +1,17 @@ +<html><title>%buffer.name% - Buffer</title><body> + +<a action="%buffer.bypass_prefix%">Home</a> > Unique > <font color=LEVEL>%unique.name%</font> (%unique.num_buffs%/%player.unique_max_buffs%, %unique.num_songs_dances%/%player.unique_max_songs_dances%)<br> + +<center> + <table width=270 cellspacing=0 cellpadding=0> + [FOREACH(buff IN unique.buffs DO + <tr> + <td width=32 height=32><img width=32 height=32 src="%buff.skill_icon%"></td> + <td><button width=238 height=32 fore=L2UI_CT1.Button_DF back=L2UI_CT1.Button_DF_Down value="%buff.skill_name%" action="%buffer.bypass_prefix% u r %unique.name% %buff.ident%"></td> + </tr> + <tr><td height=10></td><td></td></tr> + )ENDEACH] + </table> +</center> + +</body></html> \ No newline at end of file diff --git a/src/main/resources/data/service/buffer/json/documentation.txt b/src/main/resources/data/service/buffer/json/documentation.txt new file mode 100644 index 0000000000..ec416f6306 --- /dev/null +++ b/src/main/resources/data/service/buffer/json/documentation.txt @@ -0,0 +1,85 @@ +################################################################################ +# BufferService - JSON Docs # +################################################################################ + +############### +# global.json # +############### + + { + "buffs": { + "<buffId>": { + "id": "<buffId>", + "skill": (number), + "level": (number), + "type": "BUFF" or "SONG_DANCE", + "items" : [ + { "id":(number), "amount":(number) }, + .... + ] + }, + .... + }, + + "buffCategories": { + "<buffCategoryId>": { + "id": "<buffCategoryId>", + "name": (string), + "buffs": ["<buffId>", ...] + }, + .... + } + } + + Notes: + • buffs: An object where each property represents a buff (the "id" property must match the key in "buffs"). + • buffs.<buffId>.skill: skill id + • buffs.<buffId>.level: skill level + • buffs.<buffId>.type: This property must be a string of either "BUFF" or "SONG_DANCE". + • buffs.<buffId>.items: An array of item objects + • buffCategories: An object where each property represents a buff category (the "id" property must match the key in "buffCategories" + • buffCategories.<buffCategoryId>.name: display name of buff category + • buffCategories.<buffCategoryId>.buffs: property is an array of <buffId> from global.json + + +############### +# voiced.json # +############### + + { + "dialogType": "NPC" or "COMMUNITY", + "htmlFolder": (string), + "canHeal": (boolean), + "canCancel": (boolean), + + "presetBuffCategories": ["<buffCategoryId>", ...], + "buffCategories": ["<buffCategoryId>", ...] + } + + Notes: + • dialogType: NPC opens npc html window, COMMUNITY opens community board + • htmlFolder: from where to load html files + • canHeal: whatever this buffer can heal + • canCancel: whatever this buffer can cancel buffs + • presetBuffCategories & buffCategories: both properties are an array of <buffCategoryId> from global.json + +##################### +# npcs/<npcId>.json # +##################### + + { + "npcId": "<npcId>", + "directFirstTalk": (boolean), + "dialogType": "NPC" or "COMMUNITY", + "htmlFolder": (string), + "canHeal": (boolean), + "canCancel": (boolean), + + "presetBuffCategories": ["<buffCategoryId>", ...], + "buffCategories": ["<buffCategoryId>", ...] + } + + Notes: + • npcId: the npc id + • directFirstTalk: whatever to directly show the script htmls (true) or initially show htmls from <datapack>/data/html when talking to the npc by clicking on it + • see notes from voiced.json \ No newline at end of file diff --git a/src/main/resources/data/service/buffer/json/global.json b/src/main/resources/data/service/buffer/json/global.json new file mode 100644 index 0000000000..f66e93ba6f --- /dev/null +++ b/src/main/resources/data/service/buffer/json/global.json @@ -0,0 +1,197 @@ +{ + "buffs": { + "BUFF_0": {"id":"BUFF_0", "skill":1036, "level":2, "type":"BUFF","items":[{"id":57, "amount":100}]}, + "BUFF_1": {"id":"BUFF_1", "skill":1040, "level":3, "type":"BUFF", "items":[{"id":57, "amount":100}]}, + "BUFF_2": {"id":"BUFF_2", "skill":1043, "level":1, "type":"BUFF", "items":[{"id":57, "amount":100}]}, + "BUFF_3": {"id":"BUFF_3", "skill":1044, "level":3, "type":"BUFF", "items":[{"id":57, "amount":100}]}, + "BUFF_4": {"id":"BUFF_4", "skill":1045, "level":6, "type":"BUFF", "items":[{"id":57, "amount":100}]}, + "BUFF_5": {"id":"BUFF_5", "skill":1047, "level":4, "type":"BUFF", "items":[{"id":57, "amount":100}]}, + "BUFF_6": {"id":"BUFF_6", "skill":1048, "level":2, "type":"BUFF", "items":[{"id":57, "amount":100}]}, + "BUFF_7": {"id":"BUFF_7", "skill":1059, "level":6, "type":"BUFF", "items":[{"id":57, "amount":100}]}, + "BUFF_8": {"id":"BUFF_8", "skill":1068, "level":3, "type":"BUFF", "items":[{"id":57, "amount":100}]}, + "BUFF_9": {"id":"BUFF_9", "skill":1077, "level":3, "type":"BUFF", "items":[{"id":57, "amount":100}]}, + "BUFF_10": {"id":"BUFF_10", "skill":1085, "level":3, "type":"BUFF", "items":[{"id":57, "amount":100}]}, + "BUFF_11": {"id":"BUFF_11", "skill":1086, "level":2, "type":"BUFF", "items":[{"id":57, "amount":100}]}, + "BUFF_12": {"id":"BUFF_12", "skill":1087, "level":3, "type":"BUFF", "items":[{"id":57, "amount":100}]}, + "BUFF_13": {"id":"BUFF_13", "skill":1204, "level":2, "type":"BUFF", "items":[{"id":57, "amount":100}]}, + "BUFF_14": {"id":"BUFF_14", "skill":1242, "level":3, "type":"BUFF", "items":[{"id":57, "amount":100}]}, + "BUFF_15": {"id":"BUFF_15", "skill":1243, "level":6, "type":"BUFF", "items":[{"id":57, "amount":100}]}, + "BUFF_16": {"id":"BUFF_16", "skill":1257, "level":3, "type":"BUFF", "items":[{"id":57, "amount":100}]}, + "BUFF_17": {"id":"BUFF_17", "skill":1268, "level":4, "type":"BUFF", "items":[{"id":57, "amount":100}]}, + "BUFF_18": {"id":"BUFF_18", "skill":1303, "level":2, "type":"BUFF", "items":[{"id":57, "amount":100}]}, + "BUFF_19": {"id":"BUFF_19", "skill":1304, "level":3, "type":"BUFF", "items":[{"id":57, "amount":100}]}, + "BUFF_20": {"id":"BUFF_20", "skill":1307, "level":3, "type":"BUFF", "items":[{"id":57, "amount":100}]}, + "BUFF_21": {"id":"BUFF_21", "skill":1311, "level":6, "type":"BUFF", "items":[{"id":57, "amount":100}]}, + "BUFF_22": {"id":"BUFF_22", "skill":1397, "level":3, "type":"BUFF", "items":[{"id":57, "amount":100}]}, + "BUFF_23": {"id":"BUFF_23", "skill":1460, "level":1, "type":"BUFF", "items":[{"id":57, "amount":100}]}, + "BUFF_24": {"id":"BUFF_24", "skill":1240, "level":3, "type":"BUFF", "items":[{"id":57, "amount":100}]}, + + "SONG_0": {"id":"SONG_0", "skill":264, "level":1, "type":"SONG_DANCE", "items":[{"id":57, "amount":100}]}, + "SONG_1": {"id":"SONG_1", "skill":265, "level":1, "type":"SONG_DANCE", "items":[{"id":57, "amount":100}]}, + "SONG_2": {"id":"SONG_2", "skill":266, "level":1, "type":"SONG_DANCE", "items":[{"id":57, "amount":100}]}, + "SONG_3": {"id":"SONG_3", "skill":267, "level":1, "type":"SONG_DANCE", "items":[{"id":57, "amount":100}]}, + "SONG_4": {"id":"SONG_4", "skill":268, "level":1, "type":"SONG_DANCE", "items":[{"id":57, "amount":100}]}, + "SONG_5": {"id":"SONG_5", "skill":269, "level":1, "type":"SONG_DANCE", "items":[{"id":57, "amount":100}]}, + "SONG_6": {"id":"SONG_6", "skill":270, "level":1, "type":"SONG_DANCE", "items":[{"id":57, "amount":100}]}, + "SONG_7": {"id":"SONG_7", "skill":304, "level":1, "type":"SONG_DANCE", "items":[{"id":57, "amount":100}]}, + "SONG_8": {"id":"SONG_8", "skill":305, "level":1, "type":"SONG_DANCE", "items":[{"id":57, "amount":100}]}, + "SONG_9": {"id":"SONG_9", "skill":306, "level":1, "type":"SONG_DANCE", "items":[{"id":57, "amount":100}]}, + "SONG_10": {"id":"SONG_10", "skill":308, "level":1, "type":"SONG_DANCE", "items":[{"id":57, "amount":100}]}, + "SONG_11": {"id":"SONG_11", "skill":349, "level":1, "type":"SONG_DANCE", "items":[{"id":57, "amount":100}]}, + "SONG_12": {"id":"SONG_12", "skill":363, "level":1, "type":"SONG_DANCE", "items":[{"id":57, "amount":100}]}, + "SONG_13": {"id":"SONG_13", "skill":364, "level":1, "type":"SONG_DANCE", "items":[{"id":57, "amount":100}]}, + "SONG_14": {"id":"SONG_14", "skill":529, "level":1, "type":"SONG_DANCE", "items":[{"id":57, "amount":100}]}, + "SONG_15": {"id":"SONG_15", "skill":764, "level":1, "type":"SONG_DANCE", "items":[{"id":57, "amount":100}]}, + + "DANCE_0": {"id":"DANCE_0", "skill":271, "level":1, "type":"SONG_DANCE", "items":[{"id":57, "amount":100}]}, + "DANCE_1": {"id":"DANCE_1", "skill":272, "level":1, "type":"SONG_DANCE", "items":[{"id":57, "amount":100}]}, + "DANCE_2": {"id":"DANCE_2", "skill":273, "level":1, "type":"SONG_DANCE", "items":[{"id":57, "amount":100}]}, + "DANCE_3": {"id":"DANCE_3", "skill":274, "level":1, "type":"SONG_DANCE", "items":[{"id":57, "amount":100}]}, + "DANCE_4": {"id":"DANCE_4", "skill":275, "level":1, "type":"SONG_DANCE", "items":[{"id":57, "amount":100}]}, + "DANCE_5": {"id":"DANCE_5", "skill":276, "level":1, "type":"SONG_DANCE", "items":[{"id":57, "amount":100}]}, + "DANCE_6": {"id":"DANCE_6", "skill":277, "level":1, "type":"SONG_DANCE", "items":[{"id":57, "amount":100}]}, + "DANCE_7": {"id":"DANCE_7", "skill":307, "level":1, "type":"SONG_DANCE", "items":[{"id":57, "amount":100}]}, + "DANCE_8": {"id":"DANCE_8", "skill":309, "level":1, "type":"SONG_DANCE", "items":[{"id":57, "amount":100}]}, + "DANCE_9": {"id":"DANCE_9", "skill":310, "level":1, "type":"SONG_DANCE", "items":[{"id":57, "amount":100}]}, + "DANCE_10": {"id":"DANCE_10", "skill":311, "level":1, "type":"SONG_DANCE", "items":[{"id":57, "amount":100}]}, + "DANCE_11": {"id":"DANCE_11", "skill":365, "level":1, "type":"SONG_DANCE", "items":[{"id":57, "amount":100}]}, + "DANCE_12": {"id":"DANCE_12", "skill":366, "level":1, "type":"SONG_DANCE", "items":[{"id":57, "amount":100}]}, + "DANCE_13": {"id":"DANCE_13", "skill":530, "level":1, "type":"SONG_DANCE", "items":[{"id":57, "amount":100}]}, + "DANCE_14": {"id":"DANCE_14", "skill":765, "level":1, "type":"SONG_DANCE", "items":[{"id":57, "amount":100}]}, + "DANCE_15": {"id":"DANCE_15", "skill":915, "level":1, "type":"SONG_DANCE", "items":[{"id":57, "amount":100}]}, + + "CHANT_0": {"id":"CHANT_0", "skill":1002, "level":3, "type":"BUFF", "items":[{"id":57, "amount":100}]}, + "CHANT_1": {"id":"CHANT_1", "skill":1006, "level":3, "type":"BUFF", "items":[{"id":57, "amount":100}]}, + "CHANT_2": {"id":"CHANT_2", "skill":1007, "level":3, "type":"BUFF", "items":[{"id":57, "amount":100}]}, + "CHANT_3": {"id":"CHANT_3", "skill":1009, "level":3, "type":"BUFF", "items":[{"id":57, "amount":100}]}, + "CHANT_4": {"id":"CHANT_4", "skill":1251, "level":3, "type":"BUFF", "items":[{"id":57, "amount":100}]}, + "CHANT_5": {"id":"CHANT_5", "skill":1252, "level":3, "type":"BUFF", "items":[{"id":57, "amount":100}]}, + "CHANT_6": {"id":"CHANT_6", "skill":1253, "level":3, "type":"BUFF", "items":[{"id":57, "amount":100}]}, + "CHANT_7": {"id":"CHANT_7", "skill":1284, "level":3, "type":"BUFF", "items":[{"id":57, "amount":100}]}, + "CHANT_8": {"id":"CHANT_8", "skill":1308, "level":3, "type":"BUFF", "items":[{"id":57, "amount":100}]}, + "CHANT_9": {"id":"CHANT_9", "skill":1309, "level":3, "type":"BUFF", "items":[{"id":57, "amount":100}]}, + "CHANT_10": {"id":"CHANT_10", "skill":1310, "level":4, "type":"BUFF", "items":[{"id":57, "amount":100}]}, + "CHANT_11": {"id":"CHANT_11", "skill":1362, "level":1, "type":"BUFF", "items":[{"id":57, "amount":100}]}, + + "DWARVEN_0": {"id":"DWARVEN_0", "skill":825, "level":1, "type":"BUFF", "items":[{"id":57, "amount":100}]}, + "DWARVEN_1": {"id":"DWARVEN_1", "skill":826, "level":1, "type":"BUFF", "items":[{"id":57, "amount":100}]}, + "DWARVEN_2": {"id":"DWARVEN_2", "skill":827, "level":1, "type":"BUFF", "items":[{"id":57, "amount":100}]}, + "DWARVEN_3": {"id":"DWARVEN_3", "skill":828, "level":1, "type":"BUFF", "items":[{"id":57, "amount":100}]}, + "DWARVEN_4": {"id":"DWARVEN_4", "skill":829, "level":1, "type":"BUFF", "items":[{"id":57, "amount":100}]}, + "DWARVEN_5": {"id":"DWARVEN_5", "skill":830, "level":1, "type":"BUFF", "items":[{"id":57, "amount":100}]}, + + "RESIST_0": {"id":"RESIST_0", "skill":1461, "level":1, "type":"BUFF", "items":[{"id":57, "amount":100}]}, + "RESIST_1": {"id":"RESIST_1", "skill":1032, "level":3, "type":"BUFF", "items":[{"id":57, "amount":100}]}, + "RESIST_2": {"id":"RESIST_2", "skill":1033, "level":3, "type":"BUFF", "items":[{"id":57, "amount":100}]}, + "RESIST_3": {"id":"RESIST_3", "skill":1035, "level":4, "type":"BUFF", "items":[{"id":57, "amount":100}]}, + "RESIST_4": {"id":"RESIST_4", "skill":1078, "level":6, "type":"BUFF", "items":[{"id":57, "amount":100}]}, + "RESIST_5": {"id":"RESIST_5", "skill":1182, "level":3, "type":"BUFF", "items":[{"id":57, "amount":100}]}, + "RESIST_6": {"id":"RESIST_6", "skill":1189, "level":3, "type":"BUFF", "items":[{"id":57, "amount":100}]}, + "RESIST_7": {"id":"RESIST_7", "skill":1191, "level":3, "type":"BUFF", "items":[{"id":57, "amount":100}]}, + "RESIST_8": {"id":"RESIST_8", "skill":1259, "level":4, "type":"BUFF", "items":[{"id":57, "amount":100}]}, + "RESIST_9": {"id":"RESIST_9", "skill":1352, "level":1, "type":"BUFF", "items":[{"id":57, "amount":100}]}, + "RESIST_10": {"id":"RESIST_10", "skill":1353, "level":1, "type":"BUFF", "items":[{"id":57, "amount":100}]}, + "RESIST_11": {"id":"RESIST_11", "skill":1354, "level":1, "type":"BUFF", "items":[{"id":57, "amount":100}]}, + "RESIST_12": {"id":"RESIST_12", "skill":1392, "level":3, "type":"BUFF", "items":[{"id":57, "amount":100}]}, + "RESIST_13": {"id":"RESIST_13", "skill":1393, "level":3, "type":"BUFF", "items":[{"id":57, "amount":100}]}, + "RESIST_14": {"id":"RESIST_14", "skill":1548, "level":3, "type":"BUFF", "items":[{"id":57, "amount":100}]}, + + "SPECIAL_0": {"id":"SPECIAL_0", "skill":1388, "level":3, "type":"BUFF", "items":[{"id":57, "amount":100}]}, + "SPECIAL_1": {"id":"SPECIAL_1", "skill":1389, "level":3, "type":"BUFF", "items":[{"id":57, "amount":100}]}, + "SPECIAL_2": {"id":"SPECIAL_2", "skill":1499, "level":1, "type":"BUFF", "items":[{"id":57, "amount":100}]}, + "SPECIAL_3": {"id":"SPECIAL_3", "skill":1500, "level":1, "type":"BUFF", "items":[{"id":57, "amount":100}]}, + "SPECIAL_4": {"id":"SPECIAL_4", "skill":1501, "level":1, "type":"BUFF", "items":[{"id":57, "amount":100}]}, + "SPECIAL_5": {"id":"SPECIAL_5", "skill":1502, "level":1, "type":"BUFF", "items":[{"id":57, "amount":100}]}, + "SPECIAL_6": {"id":"SPECIAL_6", "skill":1503, "level":1, "type":"BUFF", "items":[{"id":57, "amount":100}]}, + "SPECIAL_7": {"id":"SPECIAL_7", "skill":1504, "level":1, "type":"BUFF", "items":[{"id":57, "amount":100}]}, + "SPECIAL_8": {"id":"SPECIAL_8", "skill":1519, "level":1, "type":"BUFF", "items":[{"id":57, "amount":100}]}, + "SPECIAL_9": {"id":"SPECIAL_9", "skill":1062, "level":2, "type":"BUFF", "items":[{"id":57, "amount":100}]}, + "SPECIAL_10": {"id":"SPECIAL_10", "skill":1355, "level":1, "type":"BUFF", "items":[{"id":57, "amount":100}]}, + "SPECIAL_11": {"id":"SPECIAL_11", "skill":1356, "level":1, "type":"BUFF", "items":[{"id":57, "amount":100}]}, + "SPECIAL_12": {"id":"SPECIAL_12", "skill":1357, "level":1, "type":"BUFF", "items":[{"id":57, "amount":100}]}, + "SPECIAL_13": {"id":"SPECIAL_13", "skill":1363, "level":1, "type":"BUFF", "items":[{"id":57, "amount":100}]}, + "SPECIAL_14": {"id":"SPECIAL_14", "skill":1413, "level":1, "type":"BUFF", "items":[{"id":57, "amount":100}]}, + "SPECIAL_15": {"id":"SPECIAL_15", "skill":1261, "level":2, "type":"BUFF", "items":[{"id":57, "amount":100}]}, + "SPECIAL_16": {"id":"SPECIAL_16", "skill":1415, "level":1, "type":"BUFF", "items":[{"id":57, "amount":100}]}, + "SPECIAL_17": {"id":"SPECIAL_17", "skill":1416, "level":1, "type":"BUFF", "items":[{"id":57, "amount":100}]}, + "SPECIAL_18": {"id":"SPECIAL_18", "skill":1542, "level":1, "type":"BUFF", "items":[{"id":57, "amount":100}]}, + "SPECIAL_19": {"id":"SPECIAL_19", "skill":1414, "level":1, "type":"BUFF", "items":[{"id":57, "amount":100}]}, + "SPECIAL_20": {"id":"SPECIAL_20", "skill":4699, "level":13, "type":"BUFF", "items":[{"id":57, "amount":100}]}, + "SPECIAL_21": {"id":"SPECIAL_21", "skill":4700, "level":13, "type":"BUFF", "items":[{"id":57, "amount":100}]}, + "SPECIAL_22": {"id":"SPECIAL_22", "skill":4702, "level":13, "type":"BUFF", "items":[{"id":57, "amount":100}]}, + "SPECIAL_23": {"id":"SPECIAL_23", "skill":4703, "level":13, "type":"BUFF", "items":[{"id":57, "amount":100}]}, + + "OVERLORD_0": {"id":"OVERLORD_0", "skill":1003, "level":3, "type":"BUFF", "items":[{"id":57, "amount":100}]}, + "OVERLORD_1": {"id":"OVERLORD_1", "skill":1004, "level":3, "type":"BUFF", "items":[{"id":57, "amount":100}]}, + "OVERLORD_2": {"id":"OVERLORD_2", "skill":1005, "level":3, "type":"BUFF", "items":[{"id":57, "amount":100}]}, + "OVERLORD_3": {"id":"OVERLORD_3", "skill":1008, "level":3, "type":"BUFF", "items":[{"id":57, "amount":100}]}, + "OVERLORD_4": {"id":"OVERLORD_4", "skill":1249, "level":3, "type":"BUFF", "items":[{"id":57, "amount":100}]}, + "OVERLORD_5": {"id":"OVERLORD_5", "skill":1250, "level":3, "type":"BUFF", "items":[{"id":57, "amount":100}]}, + "OVERLORD_6": {"id":"OVERLORD_6", "skill":1260, "level":3, "type":"BUFF", "items":[{"id":57, "amount":100}]}, + "OVERLORD_7": {"id":"OVERLORD_7", "skill":1282, "level":2, "type":"BUFF", "items":[{"id":57, "amount":100}]}, + "OVERLORD_8": {"id":"OVERLORD_8", "skill":1364, "level":1, "type":"BUFF", "items":[{"id":57, "amount":100}]}, + "OVERLORD_9": {"id":"OVERLORD_9", "skill":1365, "level":1, "type":"BUFF", "items":[{"id":57, "amount":100}]} + }, + + "buffCategories": { + "BC_FIGHTER": { + "id":"BC_FIGHTER", + "name":"Fighter", + "buffs":["DANCE_0", "DANCE_1", "DANCE_3", "DANCE_4", "DANCE_5", "DANCE_6", "DANCE_7", "DANCE_8", "DANCE_9", "DANCE_10", "DANCE_13", "DANCE_15", "SONG_0", "SONG_1", "SONG_2", "SONG_3", "SONG_4", "SONG_5", "SONG_6", "SONG_7", "SONG_8", "SONG_9", "SONG_10", "SONG_11", "SONG_12", "SONG_13", "SONG_14", "SONG_15", "BUFF_3", "BUFF_11", "BUFF_16", "BUFF_17", "BUFF_20", "BUFF_22", "BUFF_24", "RESIST_3", "RESIST_8", "RESIST_9", "RESIST_10", "RESIST_11", "SPECIAL_0", "SPECIAL_2", "SPECIAL_3", "SPECIAL_4", "SPECIAL_5", "SPECIAL_6", "SPECIAL_7", "SPECIAL_9", "SPECIAL_18"] + }, + + "BC_MAGE": { + "id":"BC_MAGE", + "name":"Mage", + "buffs":["DANCE_2", "DANCE_5", "DANCE_7", "DANCE_8", "DANCE_10", "DANCE_11", "DANCE_13", "DANCE_15", "SONG_1", "SONG_2", "SONG_3", "SONG_4", "SONG_5", "SONG_6", "SONG_7", "SONG_8", "SONG_9", "SONG_10", "SONG_11", "SONG_12", "SONG_13", "SONG_14", "SONG_15", "BUFF_3", "BUFF_10", "BUFF_11", "BUFF_16", "BUFF_18", "BUFF_20", "BUFF_22", "RESIST_4", "RESIST_5", "RESIST_6", "RESIST_7", "RESIST_8", "RESIST_9", "RESIST_10", "RESIST_11", "RESIST_12","RESIST_13", "RESIST_14", "SPECIAL_1", "SPECIAL_2", "SPECIAL_3", "SPECIAL_4", "SPECIAL_6"] + }, + + "BC_BUFFS": { + "id":"BC_BUFFS", + "name":"Buffs", + "buffs":["BUFF_0", "BUFF_1", "BUFF_2", "BUFF_3", "BUFF_4", "BUFF_5", "BUFF_6", "BUFF_7", "BUFF_8", "BUFF_9", "BUFF_10", "BUFF_11", "BUFF_12", "BUFF_13", "BUFF_14", "BUFF_15", "BUFF_16", "BUFF_17", "BUFF_18", "BUFF_19", "BUFF_20", "BUFF_21", "BUFF_22", "BUFF_23", "BUFF_24"] + }, + + "BC_SONGS": { + "id":"BC_SONGS", + "name":"Songs", + "buffs":["SONG_0", "SONG_1", "SONG_2", "SONG_3", "SONG_4", "SONG_5", "SONG_6", "SONG_7", "SONG_8", "SONG_9", "SONG_10", "SONG_11", "SONG_12", "SONG_13", "SONG_14", "SONG_15"] + }, + + "BC_DANCES": { + "id":"BC_DANCES", + "name":"Dances", + "buffs":["DANCE_0", "DANCE_1", "DANCE_2", "DANCE_3", "DANCE_4", "DANCE_5", "DANCE_6", "DANCE_7", "DANCE_8", "DANCE_9", "DANCE_10", "DANCE_11", "DANCE_12", "DANCE_13", "DANCE_14", "DANCE_15"] + }, + + "BC_CHANTS": { + "id":"BC_CHANTS", + "name":"Chants", + "buffs":["CHANT_0", "CHANT_1", "CHANT_2", "CHANT_3", "CHANT_4", "CHANT_5", "CHANT_6", "CHANT_7", "CHANT_8", "CHANT_9", "CHANT_10", "CHANT_11"] + }, + + "BC_DWARVEN": { + "id":"BC_DWARVEN", + "name":"Dwarven", + "buffs":["DWARVEN_0", "DWARVEN_1", "DWARVEN_2", "DWARVEN_3", "DWARVEN_4", "DWARVEN_5"] + }, + + "BC_RESIST": { + "id":"BC_RESIST", + "name":"Resist", + "buffs":["RESIST_0", "RESIST_1", "RESIST_2", "RESIST_3", "RESIST_4", "RESIST_5", "RESIST_6", "RESIST_7", "RESIST_8", "RESIST_9", "RESIST_10", "RESIST_11", "RESIST_12", "RESIST_13", "RESIST_14"] + }, + + "BC_SPECIAL": { + "id":"BC_SPECIAL", + "name":"Special", + "buffs":["SPECIAL_0", "SPECIAL_1", "SPECIAL_2", "SPECIAL_3", "SPECIAL_4", "SPECIAL_5", "SPECIAL_6", "SPECIAL_7", "SPECIAL_8", "SPECIAL_9", "SPECIAL_10", "SPECIAL_11", "SPECIAL_12", "SPECIAL_13", "SPECIAL_14", "SPECIAL_15", "SPECIAL_16", "SPECIAL_17", "SPECIAL_18", "SPECIAL_19", "SPECIAL_20", "SPECIAL_21", "SPECIAL_22", "SPECIAL_23"] + }, + + "BC_OVERLORD": { + "id":"BC_OVERLORD", + "name":"Overlord", + "buffs":["OVERLORD_0", "OVERLORD_1", "OVERLORD_2", "OVERLORD_3", "OVERLORD_4", "OVERLORD_5", "OVERLORD_6", "OVERLORD_7", "OVERLORD_8", "OVERLORD_9"] + } + } +} \ No newline at end of file diff --git a/src/main/resources/data/service/buffer/json/npcs/60001.json b/src/main/resources/data/service/buffer/json/npcs/60001.json new file mode 100644 index 0000000000..d028b28a0c --- /dev/null +++ b/src/main/resources/data/service/buffer/json/npcs/60001.json @@ -0,0 +1,11 @@ +{ + "npcId":60001, + "directFirstTalk":true, + "dialogType":"NPC", + "htmlFolder":"npc", + "canHeal":true, + "canCancel":true, + + "presetBuffCategories":["BC_FIGHTER", "BC_MAGE"], + "buffCategories":["BC_BUFFS","BC_SONGS","BC_DANCES","BC_CHANTS","BC_DWARVEN","BC_RESIST","BC_SPECIAL","BC_OVERLORD"] +} \ No newline at end of file diff --git a/src/main/resources/data/service/buffer/json/voiced.json b/src/main/resources/data/service/buffer/json/voiced.json new file mode 100644 index 0000000000..afcd88ccb9 --- /dev/null +++ b/src/main/resources/data/service/buffer/json/voiced.json @@ -0,0 +1,9 @@ +{ + "dialogType":"COMMUNITY", + "htmlFolder":"community", + "canHeal":true, + "canCancel":true, + + "presetBuffCategories":["BC_FIGHTER", "BC_MAGE"], + "buffCategories":["BC_BUFFS","BC_SONGS","BC_DANCES","BC_CHANTS","BC_DWARVEN","BC_RESIST","BC_SPECIAL","BC_OVERLORD"] +} \ No newline at end of file diff --git a/src/main/resources/data/stats/npcs/custom/custom_bufferservice.xml b/src/main/resources/data/stats/npcs/custom/custom_bufferservice.xml new file mode 100644 index 0000000000..65fcd9a078 --- /dev/null +++ b/src/main/resources/data/stats/npcs/custom/custom_bufferservice.xml @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="UTF-8"?> +<list xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../xsd/npcs.xsd"> + <npc id="60001" displayId="31324" name="Phoenia" usingServerSideName="true" title="Buffer" usingServerSideTitle="true" type="L2Npc"> + <collision> + <radius normal="8" /> + <height normal="23" /> + </collision> + </npc> +</list> \ No newline at end of file diff --git a/src/main/resources/sql/custom/custom_buffer_service_1_ulists.sql b/src/main/resources/sql/custom/custom_buffer_service_1_ulists.sql new file mode 100644 index 0000000000..60202f86f1 --- /dev/null +++ b/src/main/resources/sql/custom/custom_buffer_service_1_ulists.sql @@ -0,0 +1,8 @@ +CREATE TABLE `custom_buffer_service_ulists` ( + `ulist_id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `ulist_char_id` int(10) unsigned NOT NULL, + `ulist_name` varchar(255) NOT NULL, + PRIMARY KEY (`ulist_id`), + UNIQUE KEY `ulist_char_id` (`ulist_char_id`,`ulist_name`), + CONSTRAINT `custom_buffer_service_ulists_ibfk_1` FOREIGN KEY (`ulist_char_id`) REFERENCES `characters` (`charId`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; \ No newline at end of file diff --git a/src/main/resources/sql/custom/custom_buffer_service_2_ulist_buffs.sql b/src/main/resources/sql/custom/custom_buffer_service_2_ulist_buffs.sql new file mode 100644 index 0000000000..03b298aa45 --- /dev/null +++ b/src/main/resources/sql/custom/custom_buffer_service_2_ulist_buffs.sql @@ -0,0 +1,6 @@ +CREATE TABLE `custom_buffer_service_ulist_buffs` ( + `ulist_id` int(10) unsigned NOT NULL, + `ulist_buff_ident` varchar(255) NOT NULL, + PRIMARY KEY (`ulist_id`,`ulist_buff_ident`), + CONSTRAINT `custom_buffer_service_ulist_buffs_ibfk_1` FOREIGN KEY (`ulist_id`) REFERENCES `custom_buffer_service_ulists` (`ulist_id`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; \ No newline at end of file -- GitLab