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/item/tool.d, selery/item/tool.d) 28 */ 29 module selery.item.tool; 30 31 import std.conv : ConvException; 32 import std.json : JSONValue, JSON_TYPE; 33 34 import sel.nbt.tags; 35 36 import selery.about; 37 import selery.block.block : Block, Faces; 38 import selery.block.blocks : Blocks; 39 import selery.entity.entity : Entity; 40 import selery.entity.living : Living; 41 import selery.item.item : Item; 42 import selery.math.vector : BlockPosition; 43 import selery.player.player : Player; 44 import selery.util.color : Color, Colorable; 45 46 static import sul.items; 47 48 enum Durability : ushort { 49 50 gold = 31, 51 wood = 60, 52 stone = 132, 53 iron = 251, 54 diamond = 1562, 55 shears = 238, 56 fishingRod = 65, 57 flintAndSteel = 65, 58 carrotOnAStick = 26, 59 bow = 385, 60 61 } 62 63 enum Tools : ubyte { 64 65 none = 0, 66 shovel = 1, 67 pickaxe = 2, 68 axe = 3, 69 hoe = 4, 70 armor = 5, 71 shears = 6, 72 sword = 8, // flag 73 74 all = 0, 75 wood = 1, 76 stone = 2, 77 gold = 3, 78 iron = 4, 79 diamond = 5, 80 81 } 82 83 abstract class Tool : Item { 84 85 protected ushort damage; 86 87 public @safe this(E...)(E args) { 88 static if(E.length > 0 && is(typeof(E[0]) : int)) { 89 super(args[1..$]); 90 this.damage = args[0] & ushort.max; 91 } else { 92 super(args); 93 } 94 } 95 96 public override pure nothrow @property @safe @nogc ushort javaMeta() { 97 return this.damage; 98 } 99 100 public override pure nothrow @property @safe @nogc ushort bedrockMeta() { 101 return this.damage; 102 } 103 104 public override pure nothrow @property @safe @nogc bool tool() { 105 return true; 106 } 107 108 public abstract pure nothrow @property @safe @nogc ushort durability(); 109 110 public override pure nothrow @property @safe @nogc bool finished() { 111 return this.damage >= this.durability; 112 } 113 114 protected void applyDamage(ushort amount) { 115 if(!this.unbreakable) this.damage += amount; 116 } 117 118 alias unbreakable = super.unbreakable; 119 120 public override @property @safe bool unbreakable(bool unbreakable) { 121 if(super.unbreakable(unbreakable)) { 122 this.damage = 0; 123 return true; 124 } else { 125 return false; 126 } 127 } 128 129 } 130 131 class ToolItem(sul.items.Item si, ubyte _type, ubyte _material, ushort _durability, uint _attack=1) : Tool { 132 133 public @safe this(E...)(E args) { 134 super(args); 135 } 136 137 public override pure nothrow @property @safe @nogc const sul.items.Item data() { 138 return si; 139 } 140 141 public override pure nothrow @property @safe @nogc ubyte toolType() { 142 return _type; 143 } 144 145 public override pure nothrow @property @safe @nogc ubyte toolMaterial() { 146 return _material; 147 } 148 149 public override pure nothrow @property @safe @nogc ushort durability() { 150 return _durability; 151 } 152 153 public override pure nothrow @property @safe @nogc uint attack() { 154 return _attack; 155 } 156 157 alias slot this; 158 159 } 160 161 class SwordItem(sul.items.Item si, ubyte material, ushort durability, uint attack) : ToolItem!(si, Tools.sword, material, durability, attack) { 162 163 public this(E...)(E args) { 164 super(args); 165 } 166 167 public override bool destroyOn(Player player, Block block, BlockPosition position) { 168 if(!block.instantBreaking) { 169 this.applyDamage(2); 170 return true; 171 } 172 return false; 173 } 174 175 public override bool attackOnEntity(Player player, Entity entity) { 176 if(cast(Living)entity) { 177 this.applyDamage(1); 178 return true; 179 } 180 return false; 181 } 182 183 alias slot this; 184 185 } 186 187 class MiningItem(sul.items.Item si, ubyte tool, ubyte material, ushort durability, uint attack) : ToolItem!(si, tool, material, durability, attack) { 188 189 public this(E...)(E args) { 190 super(args); 191 } 192 193 public override bool destroyOn(Player player, Block block, BlockPosition position) { 194 if(!block.instantBreaking) { 195 this.applyDamage(1); 196 return true; 197 } 198 return false; 199 } 200 201 public override bool attackOnEntity(Player player, Entity entity) { 202 if(cast(Living)entity) { 203 this.applyDamage(2); 204 return true; 205 } 206 return false; 207 } 208 209 alias slot this; 210 211 } 212 213 alias PickaxeItem(sul.items.Item si, ubyte material, ushort durability, uint attack) = MiningItem!(si, Tools.pickaxe, material, durability, attack); 214 215 alias AxeItem(sul.items.Item si, ubyte material, ushort durability, uint attack) = MiningItem!(si, Tools.axe, material, durability, attack); 216 217 class ShovelItem(sul.items.Item si, ubyte material, ushort durability, uint attack) : MiningItem!(si, Tools.shovel, material, durability, attack) { 218 219 public this(E...)(E args) { 220 super(args); 221 } 222 223 public override bool useOnBlock(Player player, Block block, BlockPosition position, ubyte face) { 224 if(face != Faces.DOWN && block == Blocks.grass && player.world[position + [0, 1, 0]] == Blocks.air) { 225 player.world[position] = Blocks.grassPath; 226 this.applyDamage(1); 227 return true; 228 } 229 return false; 230 } 231 232 alias slot this; 233 234 } 235 236 class HoeItem(sul.items.Item si, ubyte material, ushort durability) : ToolItem!(si, Tools.hoe, material, durability, 1) { 237 238 public this(E...)(E args) { 239 super(args); 240 } 241 242 public override bool useOnBlock(Player player, Block block, BlockPosition position, ubyte face) { 243 if(face != Faces.DOWN && (block == [Blocks.dirt, Blocks.grass, Blocks.grassPath]) && player.world[position + [0, 1, 0]] == Blocks.air) { 244 player.world[position] = Blocks.farmland0; 245 this.applyDamage(1); 246 return true; 247 } 248 return false; 249 } 250 251 public override bool attackOnEntity(Player player, Entity entity) { 252 if(cast(Living)entity) { 253 this.applyDamage(1); 254 return true; 255 } 256 return false; 257 } 258 259 alias slot this; 260 261 } 262 263 interface Armor { 264 265 public enum ubyte helmet = 0; 266 public enum ubyte chestplate = 1; 267 public enum ubyte leggings = 2; 268 public enum ubyte boots = 3; 269 270 public alias cap = helmet; 271 public alias tunic = chestplate; 272 public alias pants = leggings; 273 274 public @property ubyte type(); 275 276 public @property uint protection(); 277 278 public bool doDamage(Player player); 279 280 } 281 282 class ArmorItem(sul.items.Item si, ushort durability, ubyte atype, uint aprotection, E...) : ToolItem!(si, Tools.armor, 0, durability, 1, E), Armor if(atype <= 3) { 283 284 public this(E...)(E args) { 285 super(args); 286 } 287 288 public override @property ubyte type() { 289 return atype; 290 } 291 292 public override @property uint protection() { 293 return aprotection; 294 } 295 296 public override bool doDamage(Player player) { 297 this.applyDamage(1); 298 return true; 299 } 300 301 alias slot this; 302 303 } 304 305 class ColorableArmorItem(sul.items.Item si, ushort durability, ubyte atype, uint aprotection) : ArmorItem!(si, durability, atype, aprotection), Colorable { 306 307 private Color m_color; 308 309 public this(E...)(E args) { 310 static if(E.length && is(typeof(E[0] == Color))) { 311 if(args[0] !is null) { 312 super(args[1..$]); 313 this.color = args[0]; 314 } 315 } else { 316 super(args); 317 } 318 } 319 320 public override @trusted void parseJSON(JSONValue data) { 321 super.parseJSON(data); 322 if(data.type == JSON_TYPE.OBJECT) { 323 auto c = "color" in data; 324 if(c) { 325 if(c.type == JSON_TYPE.STRING && c.str.length == 6) { 326 try { 327 auto color = Color.fromString(c.str); 328 if(color !is null) this.color = color; 329 } catch(ConvException) {} 330 } else if(c.type == JSON_TYPE.INTEGER) { 331 this.color = Color.fromRGB(cast(uint)c.integer); 332 } 333 } 334 } 335 } 336 337 public override void parseJavaCompound(Compound compound) { 338 super.parseJavaCompound(compound); 339 compound = compound.get!Compound("", compound); 340 auto display = compound.get!Compound("display", new Compound()); 341 if(display.has!Int("color")) this.color = Color.fromRGB(display.getValue!Int("color", 0)); 342 } 343 344 public override void parseBedrockCompound(Compound compound) { 345 super.parseBedrockCompound(compound); 346 compound = compound.get!Compound("", compound); 347 if(compound.has!Int("customColor")) this.color = Color.fromRGB(compound.getValue!Int("customColor", 0)); 348 } 349 350 /** 351 * Gets the item's custom colour. 352 */ 353 public override pure nothrow @property @safe @nogc Color color() { 354 return this.m_color; 355 } 356 357 /** 358 * Sets the item's custom colour. 359 */ 360 public override @property Color color(Color color) { 361 if(color is null) { 362 // remove 363 { 364 auto display = this.m_pc_tag.get!Compound("display", null); 365 display.remove("color"); 366 if(display.empty) { 367 this.m_pc_tag.remove("display"); 368 if(this.m_pc_tag.empty) this.m_pc_tag = null; 369 } 370 } 371 { 372 this.m_pe_tag.remove("customColor"); 373 if(this.m_pe_tag.empty) this.m_pe_tag = null; 374 } 375 } else { 376 // add 377 uint rgb = color.rgb; 378 { 379 auto cc = new Named!Int("color", rgb); 380 if(this.m_pc_tag is null) this.m_pc_tag = new Compound(new Named!Compound("display", cc)); 381 else if(!this.m_pc_tag.has!Compound("display")) this.m_pc_tag["display"] = new Compound(cc); 382 else this.m_pc_tag.get!Compound("display", null)[] = cc; 383 } 384 { 385 auto cc = new Named!Int("customColor", rgb); 386 if(this.m_pe_tag is null) this.m_pe_tag = new Compound(cc); 387 else this.m_pe_tag[] = cc; 388 } 389 } 390 return this.m_color = color; 391 } 392 393 alias slot this; 394 395 } 396 397 class PlaceableArmor(sul.items.Item si, E...) : ArmorItem!(si, 1, 0, 0) { 398 399 public this(E...)(E args) { 400 super(args); 401 } 402 403 public override pure nothrow @property @safe @nogc bool tool() { 404 return false; 405 } 406 407 public override bool doDamage(Player player) { 408 return false; 409 } 410 411 alias slot this; 412 413 }