/* * This file is part of the Illarion project. * * Copyright © 2015 - Illarion e.V. * * Illarion is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Illarion 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. */ package illarion.easynpc.parser; import illarion.common.data.Skill; import illarion.common.data.Skills; import illarion.common.types.ServerCoordinate; import illarion.easynpc.data.*; import illarion.easynpc.grammar.EasyNpcParser.*; import illarion.easynpc.parsed.talk.AdvancedNumber; import illarion.easynpc.parsed.talk.consequences.ConsequenceArena.Task; import org.antlr.v4.runtime.ParserRuleContext; import org.antlr.v4.runtime.tree.ParseTree; import org.antlr.v4.runtime.tree.TerminalNode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; /** * This class contains a collection of utility function to process the parser tree. * * @author Martin Karing <nitram@illarion.org> */ final class Utils { @Nonnull private static final Logger LOGGER = LoggerFactory.getLogger(Utils.class); private Utils() { } @Nonnull static String getString(@Nullable ParseTree node) { if (node == null) { LOGGER.warn("Node for string not found."); return "<null>"; } return removeQuotes(node.getText()); } static boolean getBoolean(@Nullable ParseTree node) { if (node == null) { LOGGER.warn("Node for boolean not found."); return false; } String string = node.getText(); switch (string) { case "true": case "on": case "yes": return true; default: return false; } } static int getInteger(@Nullable ParseTree node) { if (node == null) { LOGGER.warn("Node for integer not found."); return 0; } try { return Integer.parseInt(node.getText()); } catch (NumberFormatException e) { LOGGER.warn("Number node does not seem to contain a number."); return 0; } } static double getFloat(@Nullable ParseTree node) { if (node == null) { LOGGER.warn("Node for floating-point value not found."); return 0; } try { return Double.parseDouble(node.getText()); } catch (NumberFormatException e) { LOGGER.warn("Number node does not seem to contain a number."); return 0; } } @Nonnull private static <T extends Enum<T>> T getEnumValue( @Nullable ParserRuleContext node, @Nonnull Class<T> enumClass, @Nonnull T defaultValue) { if (node == null) { LOGGER.warn("Expected node for enumerator {} not found.", enumClass.getSimpleName()); return defaultValue; } String string = removeQuotes(node.getText()); try { return Enum.valueOf(enumClass, string); } catch (IllegalArgumentException e) { node.addErrorNode(node.getStart()); LOGGER.warn("Failed to resolve {} to enumerator {}", string, enumClass.getSimpleName()); return defaultValue; } } @Nonnull static Task getArenaTask(@Nullable ArenaTaskContext node) { if (node == null) { LOGGER.warn("Expected node for arena task not found."); return Task.RequestMonster; } switch (node.getStart().getText()) { case "requestMonster": return Task.RequestMonster; case "getStats": return Task.ShowStatistics; case "getRanking": return Task.ShowRanking; default: LOGGER.warn("Failed to resolve {} to arena task.", node.getStart().getText()); return Task.RequestMonster; } } @Nonnull static CharacterRace getRace(@Nullable RaceContext node) { return getEnumValue(node, CharacterRace.class, CharacterRace.human); } @Nonnull static CharacterSex getSex(@Nullable GenderContext node) { return getEnumValue(node, CharacterSex.class, CharacterSex.male); } @Nonnull static CharacterLanguage getCharacterLanguage(@Nullable CharLanguageContext node) { return getEnumValue(node, CharacterLanguage.class, CharacterLanguage.common); } @Nonnull static PlayerLanguage getPlayerLanguage(@Nullable LanguageContext node) { return getEnumValue(node, PlayerLanguage.class, PlayerLanguage.english); } @Nonnull static NpcBaseState getTalkState(@Nullable TalkstateGetContext node) { return getEnumValue(node, NpcBaseState.class, NpcBaseState.idle); } @Nonnull static NpcBaseStateToggle getTalkState(@Nullable TalkstateSetContext node) { return getEnumValue(node, NpcBaseStateToggle.class, NpcBaseStateToggle.begin); } @Nonnull static CharacterDirection getDirection(@Nullable DirectionContext node) { return getEnumValue(node, CharacterDirection.class, CharacterDirection.north); } @Nonnull static Towns getTown(@Nullable TownContext node) { if (node == null) { LOGGER.warn("Expected node for town not found."); return Towns.None; } switch (node.getText()) { case "none": return Towns.None; case "free": return Towns.Free; default: return getEnumValue(node, Towns.class, Towns.None); } } @Nonnull static Color getColor(@Nullable ColorContext node) { if (node == null) { LOGGER.warn("Expected node for color not found."); return new Color(0, 0, 0); } int red = getColorComponent(node.colorComponent(0)); int green = getColorComponent(node.colorComponent(1)); int blue = getColorComponent(node.colorComponent(2)); return new Color(red, green, blue); } @Nonnull static CharacterAttribute getAttribute(@Nullable AttributeContext node) { return getEnumValue(node, CharacterAttribute.class, CharacterAttribute.strength); } @Nonnull static CompareOperators getOperator(@Nullable CompareContext node) { if (node == null) { LOGGER.warn("Expected node for compare not found."); return CompareOperators.equal; } switch (node.getStart().getText()) { case "=": return CompareOperators.equal; case "<": return CompareOperators.lesser; case ">": return CompareOperators.greater; case "<=": return CompareOperators.lesserEqual; case ">=": return CompareOperators.greaterEqual; case "~=": return CompareOperators.notEqual; default: node.addErrorNode(node.getStart()); LOGGER.warn("Unexpected value {} for compare operator.", node.getText()); return CompareOperators.equal; } } @Nonnull static CalculationOperators getOperator(@Nullable SetContext node) { if (node == null) { LOGGER.warn("Expected node for set not found."); return CalculationOperators.set; } switch (node.getStart().getText()) { case "=": return CalculationOperators.set; case "+": return CalculationOperators.add; case "-": return CalculationOperators.subtract; default: node.addErrorNode(node.getStart()); LOGGER.warn("Unexpected value {} for set operator.", node.getText()); return CalculationOperators.set; } } @Nonnull static AdvancedNumber getAdvancedNumber(@Nullable AdvancedNumberContext node) { if (node == null) { LOGGER.warn("Expected node for advanced number not found."); return new AdvancedNumber(0); } AdvancedNumberExpressionContext expressionCtx = node.advancedNumberExpression(); if (expressionCtx != null) { AdvancedNumberExpressionBodyContext bodyCtx = expressionCtx.advancedNumberExpressionBody(); if (bodyCtx != null) { return new AdvancedNumber(bodyCtx.getText()); } } else { TerminalNode intValue = node.INT(); if (intValue != null) { return new AdvancedNumber(getInteger(intValue)); } else { if ("%NUMBER".equals(node.getText())) { return new AdvancedNumber(); } } } node.addErrorNode(node.getStart()); LOGGER.warn("Failed to extract advanced number from the context: {}", node.getText()); return new AdvancedNumber(0); } @Nullable static Items getItem(@Nullable TraderComplexItemIdContext node) { if (node == null) { LOGGER.warn("Expected node for item id not found."); return null; } return getItem(node.itemId()); } @Nullable static Items getItem(@Nullable ItemIdContext node) { if (node == null) { LOGGER.warn("Expected node for item id not found."); return null; } TerminalNode terminalNode = node.INT(); int id = getInteger(terminalNode); Items item = Items.valueOf(id); if (item == null) { node.addErrorNode(node.INT().getSymbol()); LOGGER.warn("Item ID {} failed to map to a item.", Integer.toString(id)); } return item; } @Nonnull static TalkingMode getTalkMode(@Nullable ParseTree node) { if (node == null) { LOGGER.warn("Expected node for talking mode not found."); return TalkingMode.Talk; } switch (node.getText()) { case "shout": case "yell": return TalkingMode.Shout; case "whisper": return TalkingMode.Whisper; default: return TalkingMode.Talk; } } @Nonnull static CharacterMagicType getMagicType(@Nullable MagictypeContext node) { return getEnumValue(node, CharacterMagicType.class, CharacterMagicType.nomagic); } @Nonnull static CharacterMagicType getMagicType(@Nullable MagictypeWithRunesContext node) { return getEnumValue(node, CharacterMagicType.class, CharacterMagicType.nomagic); } static int getQuestId(@Nullable QuestIdContext node) { if (node == null) { LOGGER.warn("Expected node for quest id not found."); return 0; } return getInteger(node.INT()); } static int getMonsterId(@Nullable MonsterIdContext node) { if (node == null) { LOGGER.warn("Expected node for item id not found."); return 0; } return getInteger(node.INT()); } static int getMonsterCount(@Nullable MonsterCountContext node) { if (node == null) { LOGGER.warn("Expected node for item id not found."); return 0; } return getInteger(node.INT()); } static int getRadius(@Nullable RadiusContext node) { if (node == null) { LOGGER.warn("Expected node for item id not found."); return 0; } return getInteger(node.INT()); } @Nullable static Skill getSkill(@Nullable SkillContext node) { if (node == null) { LOGGER.warn("Expected node for skill not found."); return null; } String skillName = node.getText(); Skill skill = Skills.getInstance().getSkill(skillName); if (skill == null) { node.addErrorNode(node.getStart()); LOGGER.warn("Skill name {} failed to map to a actual skill.", skillName); } return skill; } @Nonnull static Map<String, String> getItemDataOpt(@Nullable ItemDataListContext node) { if (node == null) { return Collections.emptyMap(); } return getItemData(node); } @Nonnull static Map<String, String> getItemData(@Nullable ItemDataListContext node) { if (node == null) { LOGGER.warn("Expected node for item data not found."); return Collections.emptyMap(); } Map<String, String> result = new HashMap<>(); List<ItemDataContext> dataValues = node.itemData(); for (ItemDataContext entry : dataValues) { getItemDataEntry(entry, result); } return result; } private static void getItemDataEntry( @Nullable ItemDataContext node, @Nonnull Map<String, String> storage) { if (node == null) { LOGGER.warn("Expected node for item data entry not found."); return; } String key = getString(node.STRING(0)); String value = getString(node.STRING(1)); storage.put(key, value); } static int getItemQualityOpt(@Nullable ItemQualityContext node) { if (node == null) { return 333; } return getInteger(node.INT()); } static int getItemQuality(@Nullable ItemQualityContext node) { if (node == null) { LOGGER.warn("Expected node for item quality entry not found."); return 333; } return getInteger(node.INT()); } @Nonnull static ItemPositions getItemPosition(@Nullable ItemPosContext node) { return getEnumValue(node, ItemPositions.class, ItemPositions.all); } private static int getColorComponent(@Nullable ColorComponentContext node) { if (node == null) { LOGGER.warn("Expected node for color component not found."); return 0; } return getInteger(node.INT()); } @Nonnull static ServerCoordinate getLocation(@Nullable LocationContext node) { if (node == null) { LOGGER.warn("Expected node for location not found."); return new ServerCoordinate(0, 0, 0); } int x = getLocationComponent(node.locationComponent(0)); int y = getLocationComponent(node.locationComponent(1)); int z = getLocationComponent(node.locationComponent(2)); return new ServerCoordinate(x, y, z); } private static int getLocationComponent(@Nullable LocationComponentContext node) { if (node == null) { LOGGER.warn("Expected node for location component not found."); return 0; } UnopContext unaryOperator = node.unop(); int value = getInteger(node.INT()); if ((unaryOperator != null) && "-".equals(unaryOperator.getText())) { return -value; } return value; } @Nonnull private static String removeQuotes(@Nonnull String string) { if ((string.charAt(0) == '"') && (string.charAt(string.length() - 1) == '"')) { return string.substring(1, string.length() - 1); } return string; } }