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/entity/entity.d, selery/entity/entity.d) 28 */ 29 module selery.entity.entity; 30 31 import core.atomic : atomicOp; 32 33 import std.algorithm : clamp; 34 import std.bitmanip : bigEndianToNative, nativeToBigEndian; 35 import std.conv : to; 36 import std.file : exists, read, write; 37 import std.math; 38 import std.random : uniform01; 39 import std..string : split, replace; 40 import std.traits : isArray, isAbstractClass; 41 import std.typecons : Tuple; 42 import std.uuid : UUID; 43 44 import selery.about; 45 import selery.block.block : Block, blockInto; 46 import selery.command.util : Position; 47 import selery.entity.metadata : Metadata; 48 import selery.event.event : EventListener; 49 import selery.event.world.damage; 50 import selery.event.world.world : WorldEvent; 51 import selery.item.slot : Slot; 52 import selery.math.vector; 53 import selery.node.server : NodeServer; 54 import selery.player.player : Player; 55 import selery.util.util : safe, call; 56 import selery.world.world : World; 57 58 static import sul.entities; 59 public import sul.entities : Entities; 60 61 /** 62 * Base abstract class for every entity. 63 */ 64 abstract class Entity : EventListener!WorldEvent { 65 66 private static uint count = -1; 67 68 public static @safe @nogc uint reserveLocalId() { 69 return count += 2; // always an odd number (starting from 1) 70 } 71 72 protected uint _id; 73 protected UUID n_uuid; 74 75 protected World n_world; 76 77 private Entity[size_t] n_watchlist; 78 private Entity[size_t] n_viewers; //edited by other entities 79 80 public bool ticking = true; 81 private tick_t n_ticks = 0; 82 83 protected bool n_alive = true; 84 85 public EntityPosition oldposition; 86 protected EntityPosition m_position; 87 protected EntityPosition m_last; 88 protected EntityPosition m_motion; 89 protected float m_yaw = 0; 90 protected float m_body_yaw = 0; 91 protected float m_pitch = 0; 92 public bool moved = false; 93 public bool motionmoved = false; 94 95 private bool n_falling= false; 96 protected bool n_on_ground; 97 private float highestPoint; 98 protected Entity last_puncher; 99 100 protected double n_eye_height; 101 102 private Entity m_vehicle; 103 private Entity m_passenger; 104 105 protected EntityAxis n_box; 106 107 public Metadata metadata; 108 109 protected uint n_data; 110 111 protected float acceleration = 0; // blocks/tick 112 protected float drag = 0; // percentage (0-1) 113 protected float terminal_velocity = 0; // blocks/tick 114 115 public this() { 116 // unusable entity 117 this._id = 0; 118 } 119 120 public this(World world, EntityPosition position) { 121 //assert(world !is null, "World can't be null"); 122 this._id = reserveLocalId(); 123 this.n_world = world; 124 this.n_uuid = cast()this.server.nextUUID; 125 this.m_position = this.m_last = this.oldposition = position; 126 this.m_motion = EntityPosition(0, 0, 0); 127 this.highestPoint = this.position.y; 128 //TODO entity dimensions in space 129 this.n_eye_height = 0; 130 this.n_box = new EntityAxis(0, 0, this.position); 131 this.metadata = new Metadata(); //TODO custom 132 } 133 134 public final pure nothrow @property @safe @nogc uint id() { 135 return this._id; 136 } 137 138 public pure nothrow @property @safe @nogc sul.entities.Entity data() { 139 return sul.entities.Entity.init; 140 } 141 142 /** 143 * Gets the entity's type in a string format. 144 * Example: 145 * --- 146 * assert(creeper.type == "creeper"); 147 * assert(witherSkull.type == "wither_skull"); 148 * --- 149 */ 150 public pure nothrow @property @safe string type() { 151 return this.data.name.replace(" ", "_"); 152 } 153 154 /** 155 * Indicates whether the entity exists in Minecraft. 156 */ 157 public pure nothrow @property @safe @nogc bool java() { 158 return this.data.java.exists; 159 } 160 161 public pure nothrow @property @safe @nogc ubyte javaId() { 162 return this.data.java.id; 163 } 164 165 /** 166 * Indicates whether the entity exists in Minecraft. 167 */ 168 public pure nothrow @property @safe @nogc bool bedrock() { 169 return this.data.bedrock.exists; 170 } 171 172 public pure nothrow @property @safe @nogc ubyte bedrockId() { 173 return this.data.bedrock.id; 174 } 175 176 /** 177 * Gets the item's width. 178 * Returns: a number higher than 0 or double.nan if the entity has no size 179 */ 180 public pure nothrow @property @safe @nogc double width() { 181 return this.data.width; 182 } 183 184 /** 185 * Gets the item's height. 186 * Returns: a number higher than 0 or double.nan if the entity has no size 187 */ 188 public pure nothrow @property @safe @nogc double height() { 189 return this.data.height; 190 } 191 192 /** 193 * Indicates whether the entity is an object. If not the entity is 194 * a mob (living entity). 195 */ 196 public pure nothrow @property @safe @nogc bool object() { 197 return this.data.object; 198 } 199 200 /** 201 * If the entity is an object gets the object's extra data used 202 * in Minecraft's SpawnObject packet. 203 */ 204 public final pure nothrow @property @safe @nogc int objectData() { 205 return this.n_data; 206 } 207 208 /** 209 * Gets the unique identifier (UUID). 210 * It's usually randomly generated when the entity is 211 * created and it can only be changed by the child classes. 212 */ 213 public final pure nothrow @property @safe @nogc UUID uuid() { 214 return this.n_uuid; 215 } 216 217 /** 218 * Gets the world the entity has been spawned into. 219 * Non-player entities should always have the same world for 220 * their whole life-cycle. 221 */ 222 public pure nothrow @property @safe @nogc World world() { 223 return this.n_world; 224 } 225 226 public pure nothrow @property @safe @nogc shared(NodeServer) server() { 227 return this.world.server; 228 } 229 230 // ticks the entity 231 public void tick() { 232 this.n_ticks++; 233 if(this.vehicle !is null && this.vehicle.dead) this.vehicle = null; 234 if(this.passenger !is null && this.passenger.dead) this.passenger = null; 235 if(this.metadata.changed) { 236 this.metadata.changed = false; 237 this.broadcastMetadata(); 238 } 239 } 240 241 /** 242 * Gets the amount of ticks for this entity. 243 * The ticks doesn't indicates the life-time of the entity, but 244 * how many times it has been ticked by its world. 245 */ 246 public final pure nothrow @property @safe @nogc tick_t ticks() { 247 return this.n_ticks; 248 } 249 250 public @property @trusted bool onFire() { 251 return this.metadata.get!("onFire", bool)(); 252 } 253 254 public @property @trusted bool onFire(bool flag) { 255 return this.metadata.set!"onFire"(flag); 256 } 257 258 public @property @trusted bool sneaking() { 259 return this.metadata.get!("sneaking", bool)(); 260 } 261 262 public @property @trusted bool sneaking(bool flag) { 263 return this.metadata.set!"sneaking"(flag); 264 } 265 266 public @property @trusted bool sprinting() { 267 return this.metadata.get!("sprinting", bool)(); 268 } 269 270 public @property @trusted bool sprinting(bool flag) { 271 return this.metadata.set!"sprinting"(flag); 272 } 273 274 public @property @trusted bool usingItem() { 275 return this.metadata.get!("usingItem", bool)(); 276 } 277 278 public @property @trusted bool actionFlag(bool flag) { 279 return this.metadata.set!"usingItem"(flag); 280 } 281 282 public @property @trusted bool invisible() { 283 return this.metadata.get!("invisible", bool)(); 284 } 285 286 public @property @trusted bool invisible(bool flag) { 287 return this.metadata.set!"invisible"(flag); 288 } 289 290 public @property @trusted string nametag() { 291 return this.metadata.get!("nametag", string)(); 292 } 293 294 public @property @trusted string nametag(string nametag) { 295 this.metadata.set!"nametag"(nametag); 296 this.metadata.set!"customName"(nametag); 297 return this.nametag; 298 } 299 300 public @property @trusted bool showNametag() { 301 return this.metadata.get!("showNametag", bool)(); 302 } 303 304 public @property @trusted bool showNametag(bool flag) { 305 this.metadata.set!"showNametag"(flag); 306 this.metadata.set!"alwaysShowNametag"(flag); 307 return flag; 308 } 309 310 public @property @trusted bool noai() { 311 return this.metadata.get!("noAi", bool)(); 312 } 313 314 public @property @trusted bool noai(bool noai) { 315 return this.metadata.set!"noAi"(noai); 316 } 317 318 /** 319 * Indicates which entities this one should and should not see. 320 * By default only the entities indicated as true by this function 321 * will be shown through the <a href="#Entity.show">show</a> function 322 * and added to the watchlist. 323 * For example, an arrow can see a painting, so Arrow.shouldSee(painting) 324 * will be true, but a painting shouldn't see an arrow, so Painting.shouldSee(arrow) 325 * will be false. 326 * This increases the performances as there are less controls, casts and operation 327 * on arrays in big worlds or chunks of worlds with an high concetration of entities. 328 */ 329 public @safe bool shouldSee(Entity entity) { 330 return true; 331 } 332 333 /** 334 * Adds an entity to the ones this entity can see. 335 * Params: 336 * entity = the entity that will be showed to this entity 337 * Returns: true if the entity has been added to the visible entities, false otherwise 338 * Example: 339 * --- 340 * foreach(Player player ; world.players) { 341 * player.show(entity); 342 * entity.show(player); 343 * } 344 * --- 345 */ 346 public @safe bool show(Entity entity) { 347 if(entity.id !in this.n_watchlist && entity.id != this.id) { 348 this.n_watchlist[entity.id] = entity; 349 entity.n_viewers[this.id] = this; 350 return true; 351 } 352 return false; 353 } 354 355 /** 356 * Checks wether this entity can see or not another entity. 357 * Example: 358 * --- 359 * if(!player.sees(arrow)) { 360 * player.show(arrow); 361 * } 362 * --- 363 */ 364 public @safe @nogc bool sees(Entity entity) { 365 return entity.id in this.n_watchlist ? true : false; 366 } 367 368 /** 369 * Hides an entity from this entity, if this entity can see it. 370 * Params: 371 * entity = the entity to be hidden 372 * Returns: true if the entity has been hidden, false otherwise 373 * Example: 374 * --- 375 * foreach(Living living ; entity.watchlist!Living) { 376 * entity.hide(living); 377 * living.hide(entity); 378 * } 379 * --- 380 */ 381 public @safe bool hide(Entity entity) { 382 if(entity.id in this.n_watchlist) { 383 this.n_watchlist.remove(entity.id); 384 entity.n_viewers.remove(this.id); 385 return true; 386 } 387 return false; 388 } 389 390 /** 391 * Gets a list of the entities that this entity can see. 392 * Example: 393 * --- 394 * // every entity 395 * auto entities = entity.watchlist; 396 * 397 * // every player 398 * auto players = entity.watchlist!Player; 399 * --- 400 */ 401 public final @property @trusted T[] watchlist(T:Entity=Entity)() { 402 static if(is(T == Entity)) { 403 return this.n_watchlist.values; 404 } else { 405 T[] ret; 406 foreach(ref Entity entity ; this.n_watchlist) { 407 if(cast(T)entity) ret ~= cast(T)entity; 408 } 409 return ret; 410 } 411 } 412 413 /** 414 * Gets a list of the entities that can see this entity. 415 * See_Also: watchlist for the examples on the usage 416 */ 417 public final @property @trusted T[] viewers(T:Entity=Entity)() { 418 static if(is(T == Entity)) { 419 return this.n_viewers.values; 420 } else { 421 T[] ret; 422 foreach(ref Entity entity ; this.n_viewers) { 423 if(cast(T)entity) ret ~= cast(T)entity; 424 } 425 return ret; 426 } 427 } 428 429 /** 430 * Despawns this entity, calling the event in the world. 431 */ 432 protected void despawn() { 433 this.world.despawn(this); 434 } 435 436 public @safe @nogc void setAsDespawned() { 437 this.n_alive = false; 438 } 439 440 /** 441 * Checks the dead/alive status of the entity. 442 * Example: 443 * --- 444 * assert(entity.alive ^ entity.dead); 445 * --- 446 */ 447 public @property @safe bool alive() { 448 return this.n_alive; 449 } 450 451 /// ditto 452 public @property @safe bool dead() { 453 return !this.n_alive; 454 } 455 456 /** 457 * Gets the 16x16 chunk the entity is in. 458 * A bigger chunk can be obtained by right-shifting the vector 459 * by the required amount. 460 * Example: 461 * --- 462 * auto chunk = entity.chunk; 463 * auto chunk128 = entity.chunk >> 3; 464 * --- 465 */ 466 public final @property @safe ChunkPosition chunk() { 467 return cast(ChunkPosition)this.position >> 4; 468 } 469 470 /** 471 * Gets the entity's position. 472 */ 473 public pure nothrow @property @safe @nogc EntityPosition position() { 474 return this.m_position; 475 } 476 477 /** 478 * Gets the entity's motion. 479 */ 480 public pure nothrow @property @safe @nogc EntityPosition motion() { 481 return this.m_motion; 482 } 483 484 /** 485 * Sets the entity's motion. 486 */ 487 public @property @safe EntityPosition motion(EntityPosition motion) { 488 this.motionmoved = true; 489 return this.m_motion = motion; 490 } 491 492 /** 493 * Checks whether or not an entity has motion. 494 * A motionless entity has every value of the motion's vector 495 * equal to 0. 496 */ 497 public @property @safe bool motionless() { 498 return this.motion == 0; 499 } 500 501 /** 502 * Sets the entity as motionless. 503 * This is equivalent to motion = EntityPosition(0). 504 */ 505 public @property @safe bool motionless(bool motionless) { 506 if(motionless) this.motion = EntityPosition(0, 0, 0); 507 return this.motionless; 508 } 509 510 /** 511 * Gets the motion as pc's velocity, ready to be encoded 512 * in a packet. 513 * Due to this encoding limitation, the entity's motion should 514 * never be higher than 4.096 (2^15 / 8000). 515 * If it is, it will be clamped. 516 */ 517 public @property @safe Vector3!short velocity() { 518 auto ret = this.motion * 8000; 519 return Vector3!short(clamp(ret.x, short.min, short.max), clamp(ret.y, short.min, short.max), clamp(ret.z, short.min, short.max)); 520 } 521 522 /** 523 * Gets the entity's looking direction (right-left). 524 * The value should always be in range 0..360. 525 */ 526 public final pure nothrow @property @safe @nogc float yaw() { 527 return this.m_yaw; 528 } 529 530 /** 531 * Gets the yaw as an unsigned byte for encoding reasons. 532 * To obtain a valid value the yaw should be in its valid range 533 * from 0 to 360. 534 */ 535 public final @property @safe ubyte angleYaw() { 536 return safe!ubyte(this.yaw / 360 * 256); 537 } 538 539 /** 540 * Gets the entity's body facing direction. This variable 541 * may not affect all the entities. 542 */ 543 public pure nothrow @property @safe @nogc float bodyYaw() { 544 return this.m_body_yaw; 545 } 546 547 public final @property @safe ubyte angleBodyYaw() { 548 return safe!ubyte(this.bodyYaw / 360 * 256); 549 } 550 551 /** 552 * Gets the entity's looking direction (up-down). 553 * The value should be in range -90..90 (90 included). 554 */ 555 public final pure nothrow @property @safe @nogc float pitch() { 556 return this.m_pitch; 557 } 558 559 /** 560 * Gets the pitch as a byte for encoding reasons. 561 * To btain a valid value the pitch should be in its valid range 562 * from -90 to 90. 563 */ 564 public final @property @safe byte anglePitch() { 565 return safe!byte(this.pitch / 90 * 64); 566 } 567 568 /** 569 * Boolean value indicating whether or not the player is touching 570 * the ground or is in it. 571 * This value is true even if the player is in a liquid (like water). 572 */ 573 public final pure nothrow @property @safe @nogc bool onGround() { 574 return this.n_on_ground; 575 } 576 577 /** 578 * Gets the player's looking direction calculated from yaw and pitch. 579 * The return value is in range 0..1 and it should be multiplied to 580 * obtain the desired value. 581 */ 582 public final @property @safe EntityPosition direction() { 583 float y = -sin(this.pitch * PI / 180f); 584 float xz = cos(this.pitch * PI / 180f); 585 float x = -xz * sin(this.yaw * PI / 180f); 586 float z = xz * cos(this.yaw * PI / 180f); 587 return EntityPosition(x, y, z); 588 } 589 590 /** 591 * Moves the entity. 592 */ 593 public @safe void move(EntityPosition position) { 594 if(this.position != position) { 595 this.n_box.update(position); 596 } 597 this.m_position = position; 598 this.moved = true; 599 } 600 601 public void move(EntityPosition position, float yaw, float pitch) { 602 this.m_yaw = yaw; 603 this.m_pitch = pitch; 604 this.move(position); 605 } 606 607 public void move(EntityPosition position, float yaw, float bodyYaw, float pitch) { 608 this.m_body_yaw = bodyYaw; 609 this.move(position, yaw, pitch); 610 } 611 612 /** 613 * Teleports the entity. 614 */ 615 public void teleport(EntityPosition position) { 616 this.n_box.update(position); 617 this.m_position = position; 618 this.moved = true; 619 } 620 621 /// ditto 622 public void teleport(EntityPosition position, float yaw, float pitch) { 623 this.m_yaw = yaw; 624 this.m_pitch = pitch; 625 this.teleport(position); 626 } 627 628 /// ditto 629 public void teleport(EntityPosition position, float yaw, float bodyYaw, float pitch) { 630 this.m_body_yaw = bodyYaw; 631 this.teleport(position, yaw, pitch); 632 } 633 634 /// ditto 635 public void teleport(World world, EntityPosition position) { 636 //TODO 637 } 638 639 /// ditto 640 public void teleport(World world, EntityPosition position, float yaw, float pitch) { 641 this.m_yaw = yaw; 642 this.m_pitch = pitch; 643 this.teleport(world, position); 644 } 645 646 /// ditto 647 public void teleport(World world, EntityPosition position, float yaw, float bodyYaw, float pitch) { 648 this.m_body_yaw = bodyYaw; 649 this.teleport(world, position, yaw, pitch); 650 } 651 652 /// ditto 653 public void teleport(Position position) { 654 this.teleport(position.from(this.position)); 655 } 656 657 /// ditto 658 public void teleport(Position position, float yaw, float pitch) { 659 this.teleport(position.from(this.position), yaw, pitch); 660 } 661 662 /// ditto 663 public void teleport(Position position, float yaw, float bodyYaw, float pitch) { 664 this.teleport(position.from(this.position), yaw, bodyYaw, pitch); 665 } 666 667 /// ditto 668 public void teleport(World world, Position position) { 669 this.teleport(world, position.from(this.position)); 670 } 671 672 /// ditto 673 public void teleport(World world, Position position, float yaw, float pitch) { 674 this.teleport(world, position.from(this.position), yaw, pitch); 675 } 676 677 /// ditto 678 public void teleport(World world, Position position, float yaw, float bodyYaw, float pitch) { 679 this.teleport(world, position.from(this.position), yaw, bodyYaw, pitch); 680 } 681 682 /** 683 * Gets entity's sizes. 684 */ 685 public final pure nothrow @property @safe @nogc double eyeHeight() { 686 return this.n_eye_height; 687 } 688 689 /** 690 * Does the onGround updates. 691 */ 692 protected void updateGroundStatus() { 693 if(this.position.y >= this.m_last.y) { 694 // going up 695 this.highestPoint = this.position.y; 696 this.n_falling = false; 697 if(this.position.y != this.m_last.y) this.n_on_ground = false; 698 } else { 699 // free falling (check collision with the terrain) 700 this.n_falling = true; 701 this.n_on_ground = false; 702 auto min = this.n_box.minimum; 703 auto max = this.n_box.maximum; 704 foreach(int x ; min.x.blockInto..max.x.blockInto+1) { 705 foreach(int z ; min.z.blockInto..max.z.blockInto+1) { 706 BlockPosition position = BlockPosition(x, to!int(this.position.y) - (to!int(this.position.y) == this.position.y ? 1 : 0), z); 707 auto block = this.world[position]; 708 if(block.hasBoundingBox) { 709 block.box.update(position.entityPosition); 710 if(block.box.intersects(this.n_box)) { 711 this.n_on_ground = true; 712 this.doFallDamage(this.highestPoint - this.position.y, block.fallDamageModifier); 713 this.highestPoint = this.position.y; 714 this.last_puncher = null; 715 goto BreakAll; 716 } 717 } 718 } 719 } 720 } 721 BreakAll: 722 this.m_last = this.position; 723 } 724 725 protected @trusted void doFallDamage(float distance, float modifier=1) { 726 int damage = to!int(round((distance - 3) * modifier)); 727 if(damage > 0) { 728 if(this.last_puncher is null) { 729 this.attack(new EntityFallDamageEvent(this, damage)); 730 } else { 731 this.attack(new EntityDoomedToFallEvent(this, this.last_puncher, damage)); 732 } 733 } 734 } 735 736 /** 737 * Boolean value indicating whether or not the entity 738 * is falling. 739 */ 740 public final pure nothrow @property @safe @nogc bool falling() { 741 return !this.onGround && this.n_falling; 742 } 743 744 /** 745 * Does physic movements using the entity's parameters. 746 */ 747 protected @safe void doPhysic() { 748 749 // update the motion 750 if(this.acceleration != 0) this.motion = this.motion - [0, this.acceleration, 0]; 751 if(this.motion.y.abs > this.terminal_velocity) this.m_motion = EntityPosition(this.m_motion.x, this.motion.y > 0 ? this.terminal_velocity : -this.terminal_velocity, this.m_motion.z); 752 753 // move 754 this.move(this.position + this.motion/*, atan2(this.motion.x, this.motion.z) * 180f / PI, atan2(this.motion.y, sqrt(this.motion.x * this.motion.x + this.motion.z * this.motion.z)) * 180f / PI*/); 755 756 // apply the drag force 757 if(this.drag != 0) this.motion = this.motion * (1f - this.drag); 758 759 } 760 761 /** 762 * Checks collisions with the entities in the watchlist 763 * and calls onCollideWithEntity on collision. 764 */ 765 protected void checkCollisionsWithEntities() { 766 foreach(ref Entity entity ; this.viewers) { 767 if(entity.box.intersects(this.n_box) && this.onCollideWithEntity(entity)) return; 768 } 769 } 770 771 /** 772 * Function called from checkCollisionWithEntities when 773 * this entity collides with another entity. 774 * Returns: false if the calling function should check for more collisions, true otherwise. 775 */ 776 protected bool onCollideWithEntity(Entity entity) { 777 return false; 778 } 779 780 protected void checkCollisionsWithBlocks() { 781 auto min = this.n_box.minimum; 782 auto max = this.n_box.maximum; 783 foreach(int x ; min.x.blockInto..max.x.blockInto+1) { 784 foreach(int y ; min.y.blockInto..max.y.blockInto+1) { 785 foreach(int z ; min.z.blockInto..max.z.blockInto+1) { 786 auto position = BlockPosition(x, y, z); 787 auto block = this.world[position]; 788 if(block.hasBoundingBox) { 789 block.box.update(position.entityPosition); 790 if(block.box.intersects(this.n_box) && this.onCollideWithBlock(block, position, 0)) return; 791 } 792 } 793 } 794 } 795 } 796 797 protected bool onCollideWithBlock(Block block, BlockPosition position, uint face) { 798 return false; 799 } 800 801 /** 802 * Updates the size of the entity and its bounding box. 803 */ 804 public @safe void setSize(float width, float height) { 805 this.n_box.update(width, height); 806 } 807 808 /** 809 * Gets the entity's bounding box. 810 */ 811 public final @property @safe @nogc EntityAxis box() { 812 return this.n_box; 813 } 814 /++ 815 /** 816 * Gets the entity's width. 817 */ 818 public final @property @safe @nogc float width() { 819 return this.box.width; 820 } 821 822 /** 823 * Gets the entity's height. 824 */ 825 public final @property @safe @nogc float height() { 826 return this.box.height; 827 } 828 ++/ 829 830 public final pure nothrow @property @safe @nogc float scale() { 831 return 1f; 832 } 833 834 public final @property float scale(float scale) { 835 version(Minecraft) { 836 // throw exception 837 } 838 version(Pocket) { 839 840 } 841 return 1f; 842 } 843 844 /** Gets the entity's vechicle. */ 845 public @property @safe @nogc Entity vehicle() { 846 return this.m_vehicle; 847 } 848 849 /** Sets the entity's vehicle. */ 850 protected @property @safe Entity vehicle(Entity vehicle) { 851 return this.m_vehicle = vehicle; 852 } 853 854 /** Gets the entity's passenger. */ 855 public @property @safe Entity passenger() { 856 return this.m_passenger; 857 } 858 859 /** 860 * Sets the entity's passenger. 861 * The vehicle of the passenger is set automatically. 862 * Example: 863 * --- 864 * Player player = "steve".player; 865 * Boat boat = world.spawn!Boat; 866 * 867 * boat.passenger = player; 868 * assert(player.vehicle == boat); 869 * --- 870 */ 871 public @property Entity passenger(Entity passenger) { 872 if(passenger !is null) { 873 passenger.vehicle = this; 874 this.viewers!Player.call!"sendPassenger"(cast(ubyte)3u, passenger.id, this.id); 875 } else if(this.passenger !is null) { 876 this.passenger.vehicle = null; 877 this.viewers!Player.call!"sendPassenger"(cast(ubyte)0u, this.passenger.id, this.id); 878 } 879 this.m_passenger = passenger; 880 return this.m_passenger; 881 } 882 883 /** 884 * Attacks an entity and returns the event used. 885 */ 886 public T attack(T:EntityDamageEvent)(T event) if(is(T == class) && !isAbstractClass!T) { 887 if(this.validateAttack(event)) { 888 this.world.callEvent(event); 889 if(!event.cancelled) this.attackImpl(event); 890 } else { 891 event.cancel(); 892 } 893 return event; 894 } 895 896 protected bool validateAttack(EntityDamageEvent event) { 897 return false; 898 } 899 900 protected void attackImpl(EntityDamageEvent event) { 901 902 } 903 904 /** 905 * Send the metadata to the viewers 906 */ 907 protected void broadcastMetadata() { 908 Player[] players = this.viewers!Player; 909 if(players.length > 0) { 910 foreach(ref Player player ; players) { 911 player.sendMetadata(this); 912 } 913 } 914 } 915 916 /** 917 * Drop an item from this entity 918 */ 919 public void drop(Slot slot) { 920 float f0 = uniform01!float(this.world.random) * PI * 2f; 921 float f1 = uniform01!float(this.world.random) * .02f; 922 this.world.drop(slot, this.position + [0, this.eyeHeight - .3, 0], this.direction * .3f + [cos(f0) * f1, (uniform01!float(this.world.random) - uniform01!float(this.world.random)) * .1f + .1f, sin(f0) * f1]); 923 } 924 925 public @property @safe string name() { 926 return typeid(this).to!string.split(".")[$-1]; 927 } 928 929 public @property @safe string displayName() { 930 return this.name; 931 } 932 933 public override @safe bool opEquals(Object o) { 934 return cast(Entity)o && (cast(Entity)o).id == this.id; 935 } 936 937 public override @safe string toString() { 938 return typeid(this).to!string ~ "(" ~ to!string(this.id) ~ ")"; 939 } 940 941 } 942 943 enum Rotation : float { 944 945 KEEP = float.nan, 946 947 // yaw 948 WEST = 0, 949 NORTH = 90, 950 EAST = 180, 951 SOUTH = 270, 952 953 // pitch 954 DOWN = 90, 955 FRONT = 0, 956 UP = -90, 957 958 } 959 960 /** 961 * A template for entities with changes on variables. 962 * Example: 963 * --- 964 * // an entity on fire by default 965 * alias OnFire(T) = Changed!(T, "this.onFire = true;"); 966 * Creeper creeper = world.spawn!(OnFire!Creeper); 967 * assert(creeper.onFire); 968 * 969 * // multiple changes can be used togheter 970 * new OnFire!(Unticked!(Noai!Creeper))(); 971 * --- 972 */ 973 template VariableChanged(T:Entity, string changes) { 974 975 class VariableChanged : T { 976 977 public @safe this(E...)(E args) { 978 super(args); 979 mixin(changes); 980 } 981 982 } 983 984 //alias VariableChanged = X; 985 986 } 987 988 /** 989 * An entity without ticking. 990 */ 991 alias Unticked(T:Entity) = VariableChanged!(T, "this.ticking = false;"); 992 993 /** 994 * An Entity without AI. 995 */ 996 alias Noai(T:Entity) = VariableChanged!(T, "this.noai = true"); 997 998 /** 999 * An entity without ticking and AI. 1000 */ 1001 alias UntickedNoai(T:Entity) = VariableChanged!(T, "this.ticking = false;this.noai = true;"); 1002 1003 /** 1004 * A template for entities with changing on functions. 1005 */ 1006 template FunctionChanged(T:Entity, string changes) { 1007 1008 class FunctionChanged : T { 1009 1010 mixin(changes); 1011 1012 } 1013 1014 } 1015 1016 /** 1017 * An entity without physic. 1018 */ 1019 alias NoPhysic(T:Entity) = FunctionChanged!(T, "protected override void doPhysic(){}"); 1020 1021 /** 1022 * An entity that doesn't take fall damage. 1023 */ 1024 alias NoFallDamage(T:Entity) = FunctionChanged!(T, "protected override void doFallDamage(float distance){}");