1 /*
2  * Copyright (c) 2017-2019 sel-project
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a copy
5  * of this software and associated documentation files (the "Software"), to deal
6  * in the Software without restriction, including without limitation the rights
7  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8  * copies of the Software, and to permit persons to whom the Software is
9  * furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in all
12  * copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20  * SOFTWARE.
21  *
22  */
23 /**
24  * Copyright: Copyright (c) 2017-2019 sel-project
25  * License: MIT
26  * Authors: Kripth
27  * Source: $(HTTP github.com/sel-project/selery/source/selery/enchantment/enchantment.d, selery/enchantment/enchantment.d)
28  */
29 module selery.enchantment.enchantment;
30 
31 import std.algorithm : min;
32 import std.conv : to;
33 import std.regex : ctRegex, replaceAll;
34 import std..string : toLower, replace, startsWith;
35 
36 import roman : fromRoman;
37 
38 static import sel.data.enchantment;
39 import sel.data.enchantment : Enchantments;
40 
41 /**
42  * Class that represents an enchantment and its level.
43  * Example:
44  * ---
45  * auto e = new Enchantment(Enchantments.sharpness, "V");
46  * Enchantment.fromString("luck of the sea", 5);
47  * assert(e.bedrock && e.bedrock.id == 9);
48  * assert(e.minecraft.id == 16);
49  * assert(!Enchantment.fromMinecraft(71).pocket);
50  * ---
51  */
52 final class Enchantment {
53 	
54 	private static const(sel.data.enchantment.Enchantment)[string] strings;
55 	private static const(sel.data.enchantment.Enchantment)[ubyte] _java, _bedrock;
56 	
57 	public static this() {
58 		foreach(e ; __traits(allMembers, Enchantments)) {
59 			mixin("alias ench = Enchantments." ~ e ~ ";");
60 			strings[ench.name.replace(" ", "_")] = ench;
61 			if(ench.java) _java[ench.java.id] = ench;
62 			if(ench.bedrock) _bedrock[ench.bedrock.id] = ench;
63 		}
64 	}
65 	
66 	/**
67 	 * Creates an enchantment from a string.
68 	 * Example:
69 	 * ---
70 	 * Enchantment.fromString("sharpness", 1);
71 	 * Enchantment.fromString("Fire Protection", 4);
72 	 * Enchantment.fromString("silk-touch", 1);
73 	 * Enchantment.fromString("minecraft:protection", 2);
74 	 * ---
75 	 */
76 	public static @safe Enchantment fromString(string name, ubyte level) {
77 		if(name.startsWith("minecraft:")) name = name[10..$];
78 		auto ret = name.toLower.replaceAll(ctRegex!`[ \-]`, "_") in strings;
79 		return ret ? new Enchantment(*ret, level) : null;
80 	}
81 	
82 	/**
83 	 * Creates an enchantment using its Minecraft: Java Edition's id.
84 	 * Example:
85 	 * ---
86 	 * assert(Enchantment.fromJava(9, 1).name == "frost walker");
87 	 * ---
88 	 */
89 	public static @safe Enchantment fromJava(ubyte id, ubyte level) {
90 		auto ret = id in _java;
91 		return ret ? new Enchantment(*ret, level) : null;
92 	}
93 	
94 	/**
95 	 * Creates an enchantment using its Minecraft's id.
96 	 * Example:
97 	 * ---
98 	 * assert(Enchantment.fromBedrock(9, 2).name == "sharpness");
99 	 * ---
100 	 */
101 	public static @safe Enchantment fromBedrock(ubyte id, ubyte level) {
102 		auto ret = id in _bedrock;
103 		return ret ? new Enchantment(*ret, level) : null;
104 	}
105 	
106 	public const sel.data.enchantment.Enchantment enchantment;
107 	public immutable ubyte level;
108 	
109 	public pure nothrow @safe @nogc this(sel.data.enchantment.Enchantment enchantment, ubyte level) {
110 		this.enchantment = enchantment;
111 		this.level = level == 0 ? ubyte(1) : level;
112 	}
113 	
114 	public @safe this(sel.data.enchantment.Enchantment enchantment, string level) {
115 		this(enchantment, level.fromRoman & 255);
116 	}
117 	
118 	/**
119 	 * Gets the enchantment's id. SEL currently uses Minecraft's
120 	 * id to uniquely identify an enchantment.
121 	 * Example:
122 	 * ---
123 	 * auto e = Enchantment.fromString("sharpness", 5);
124 	 * assert(e.id == e.minecraft.id);
125 	 * ---
126 	 */
127 	public pure nothrow @property @safe @nogc ubyte id() {
128 		return this.enchantment.java.id;
129 	}
130 	
131 	public override bool opEquals(Object o) {
132 		auto e = cast(Enchantment)o;
133 		return e !is null && this.id == e.id && this.level == e.level;
134 	}
135 	
136 	alias enchantment this;
137 	
138 }
139 
140 /**
141  * Exception thrown when an enchantment does not exist
142  * or is used in the wrong way.
143  */
144 class EnchantmentException : Exception {
145 	
146 	public @safe this(string message, string file=__FILE__, size_t line=__LINE__) {
147 		super(message, file, line);
148 	}
149 	
150 }