Implement EntityLiving -> EntityLiving damage & block break sfx / effects
This commit is contained in:
parent
4afb4a0633
commit
328ddca458
13 changed files with 129 additions and 27 deletions
|
@ -23,6 +23,11 @@ import { ItemStack } from "./inventories/ItemStack";
|
|||
import { PacketWindowItems } from "./packets/WindowItems";
|
||||
import { Block } from "./blocks/Block";
|
||||
import { EntityItem } from "./entities/EntityItem";
|
||||
import AABB from "./AABB";
|
||||
import { PacketSoundEffect } from "./packets/SoundEffect";
|
||||
import { SoundEffects } from "./enums/SoundEffects";
|
||||
import { PacketUseEntity } from "./packets/UseEntity";
|
||||
import { EntityLiving } from "./entities/EntityLiving";
|
||||
|
||||
export class MPClient {
|
||||
private readonly mcServer:MinecraftServer;
|
||||
|
@ -72,6 +77,7 @@ export class MPClient {
|
|||
|
||||
switch (packetId) {
|
||||
case Packet.Chat: this.handleChat(new PacketChat().readData(reader)); break;
|
||||
case Packet.UseEntity: this.handleUseEntity(new PacketUseEntity().readData(reader)); break;
|
||||
case Packet.Respawn: this.handlePacketRespawn(new PacketRespawn().readData(reader)); break;
|
||||
case Packet.Player: this.handlePacketPlayer(new PacketPlayer().readData(reader)); break;
|
||||
case Packet.PlayerPosition: this.handlePacketPlayerPosition(new PacketPlayerPosition().readData(reader)); break;
|
||||
|
@ -92,6 +98,16 @@ export class MPClient {
|
|||
}
|
||||
}
|
||||
|
||||
private handleUseEntity(packet:PacketUseEntity) {
|
||||
const attacker = this.entity.world.entites.get(packet.userId);
|
||||
const target = this.entity.world.entites.get(packet.targetId);
|
||||
if (attacker && target && target instanceof EntityLiving) {
|
||||
if (packet.leftClick) {
|
||||
target.damageFrom(2, attacker);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private handleChat(packet:PacketChat) {
|
||||
const message = packet.message.split(" ");
|
||||
if (message[0].startsWith("/")) {
|
||||
|
@ -180,11 +196,16 @@ export class MPClient {
|
|||
this.entity.world.setBlockWithNotify(this.diggingAt.x, this.diggingAt.y, this.diggingAt.z, 0);
|
||||
//this.inventory.addItemStack(new ItemStack(Block.blockBehaviours[brokenBlockId].droppedItem(brokenBlockId), 1, metadata));
|
||||
//this.send(new PacketWindowItems(0, this.inventory.getInventorySize(), this.inventory.constructInventoryPayload()).writeData());
|
||||
const itemId = Block.blockBehaviours[brokenBlockId].droppedItem(brokenBlockId);
|
||||
const blockBehaviour = Block.blockBehaviours[brokenBlockId];
|
||||
const itemId = blockBehaviour.droppedItem(brokenBlockId);
|
||||
if (itemId !== -1) {
|
||||
const itemEntity = new EntityItem(this.entity.world, new ItemStack(itemId, 1, metadata));
|
||||
itemEntity.position.set(x + 0.5, y + 0.5, z + 0.5);
|
||||
this.entity.world.addEntity(itemEntity);
|
||||
const itemCount = blockBehaviour.droppedCount(brokenBlockId);
|
||||
for (let i = 0; i < ((itemCount - 1) >> 6) + 1; i++) {
|
||||
const itemEntity = new EntityItem(this.entity.world, new ItemStack(itemId, Math.min(itemCount - 64 * i, 64), metadata));
|
||||
itemEntity.position.set(x + 0.5, y + 0.5, z + 0.5);
|
||||
this.entity.world.addEntity(itemEntity);
|
||||
this.entity.sendToNearby(new PacketSoundEffect(SoundEffects.BLOCK_BREAK, x, y, z, brokenBlockId).writeData());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -220,6 +241,10 @@ export class MPClient {
|
|||
this.diggingAt.set(packet.x, packet.y, packet.z);
|
||||
this.mapCoordsFromFace(this.diggingAt, packet.face);
|
||||
|
||||
if (this.entity.entityAABB.intersects(AABB.getAABB(this.diggingAt.x, this.diggingAt.y, this.diggingAt.z, this.diggingAt.x + 1, this.diggingAt.y + 1, this.diggingAt.z + 1))) {
|
||||
return;
|
||||
}
|
||||
|
||||
const itemStack = this.getHeldItemStack();
|
||||
if (itemStack == null || itemStack.size == 0) {
|
||||
return;
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import AABB from "../AABB";
|
||||
import { World } from "../World";
|
||||
import { BlockBehaviour } from "./BlockBehaviour";
|
||||
import { BlockBehaviourClay } from "./BlockBehaviourClay";
|
||||
import { BlockBehaviourFlower } from "./BlockBehaviourFlower";
|
||||
import { BlockBehaviourGrass } from "./BlockBehaviourGrass";
|
||||
import { BlockBehaviourStone } from "./BlockBehaviourStone";
|
||||
|
@ -15,6 +16,8 @@ abstract class Behaviour {
|
|||
|
||||
public static tallGrass = new BlockBehaviourTallGrass();
|
||||
public static flower = new BlockBehaviourFlower();
|
||||
|
||||
public static clay = new BlockBehaviourClay();
|
||||
}
|
||||
|
||||
export class Block {
|
||||
|
@ -98,6 +101,10 @@ export class Block {
|
|||
this.behaviour.droppedItem(blockId);
|
||||
}
|
||||
|
||||
public droppedCount(blockId:number) {
|
||||
this.behaviour.droppedCount(blockId);
|
||||
}
|
||||
|
||||
public getHardness() {
|
||||
return this.hardness;
|
||||
}
|
||||
|
@ -149,7 +156,7 @@ export class Block {
|
|||
static readonly flowerDandelion = new Block(37).setHardness(0).setLightPassage(255).setBehaviour(Behaviour.flower).setBlockName("Dandelion");
|
||||
static readonly flowerRose = new Block(38).setHardness(0).setLightPassage(255).setBehaviour(Behaviour.flower).setBlockName("Rose");
|
||||
|
||||
static readonly clay = new Block(82).setHardness(0.6).setBlockName("Clay");
|
||||
static readonly clay = new Block(82).setHardness(0.6).setBehaviour(Behaviour.clay).setBlockName("Clay");
|
||||
|
||||
static readonly netherrack = new Block(87).setHardness(0.4).setBlockName("Netherrack");
|
||||
}
|
|
@ -5,5 +5,6 @@ import { IBlockBehaviour } from "./IBlockBehaviour";
|
|||
export class BlockBehaviour implements IBlockBehaviour {
|
||||
public neighborBlockChange(world:World, x:number, y:number, z:number, blockId:number) {}
|
||||
public droppedItem(blockId:number) { return blockId; }
|
||||
public droppedCount(blockId:number) { return 1; }
|
||||
public getBoundingBox(x:number, y:number, z:number) { return AABB.getAABB(0 + x, 0 + y, 0 + z, 1 + x, 1 + y, 1 + z); }
|
||||
}
|
12
server/blocks/BlockBehaviourClay.ts
Normal file
12
server/blocks/BlockBehaviourClay.ts
Normal file
|
@ -0,0 +1,12 @@
|
|||
import { Item } from "../items/Item";
|
||||
import { BlockBehaviour } from "./BlockBehaviour";
|
||||
|
||||
export class BlockBehaviourClay extends BlockBehaviour {
|
||||
public droppedItem(blockId:number) {
|
||||
return Item.clay.shiftedItemID;
|
||||
}
|
||||
|
||||
public droppedCount(blockId:number) {
|
||||
return 4;
|
||||
}
|
||||
}
|
|
@ -4,5 +4,6 @@ import { World } from "../World";
|
|||
export interface IBlockBehaviour {
|
||||
neighborBlockChange(world:World, x:number, y:number, z:number, blockId:number): void,
|
||||
droppedItem: (blockId:number) => number,
|
||||
droppedCount: (blockId:number) => number,
|
||||
getBoundingBox: (x:number, y:number, z:number) => AABB,
|
||||
}
|
|
@ -114,10 +114,10 @@ export class Entity implements IEntity {
|
|||
.writeByte(this.health);
|
||||
}
|
||||
|
||||
collidesWithPlayer(aabb:AABB) {
|
||||
async collidesWithPlayer(aabb:AABB) {
|
||||
let collidedWith:Player | undefined;
|
||||
this.world.players.forEach(player => {
|
||||
if (this.entityAABB.intersects(player.entityAABB)) {
|
||||
await this.world.players.forEach(player => {
|
||||
if (this.entityAABB.intersects(player.entityAABB) && collidedWith == undefined) {
|
||||
collidedWith = player;
|
||||
}
|
||||
});
|
||||
|
@ -166,6 +166,8 @@ export class Entity implements IEntity {
|
|||
|
||||
if (entity === undefined) {
|
||||
this.health -= damage;
|
||||
} else {
|
||||
this.health -= damage;
|
||||
}
|
||||
|
||||
this.wasHurt = true;
|
||||
|
@ -249,18 +251,21 @@ export class Entity implements IEntity {
|
|||
|
||||
moveEntity(motionX:number, motionY:number, motionZ:number) {
|
||||
this.positionBeforeMove.set(this.position);
|
||||
const blockId = this.chunk.getBlockId(Math.floor(this.positionBeforeMove.x) & 0xf, Math.floor(this.positionBeforeMove.y), Math.floor(this.positionBeforeMove.z) & 0xf);
|
||||
const blockUnderEntity = blockId > 0 ? Block.blocks[blockId] : null;
|
||||
|
||||
this.position.add(motionX, motionY, motionZ);
|
||||
|
||||
this.entityAABB.move(this.position);
|
||||
|
||||
const blockId = this.chunk.getBlockId(Math.floor(this.position.x) & 0xf, Math.floor(this.position.y), Math.floor(this.position.z) & 0xf);
|
||||
const blockUnderEntity = blockId > 0 ? Block.blocks[blockId] : null;
|
||||
|
||||
if (blockUnderEntity !== null) {
|
||||
const blockBoundingBox = blockUnderEntity.getBoundingBox(Math.floor(this.positionBeforeMove.x), Math.floor(this.positionBeforeMove.y), Math.floor(this.positionBeforeMove.z));
|
||||
const blockBoundingBox = blockUnderEntity.getBoundingBox(Math.floor(this.position.x), Math.floor(this.position.y), Math.floor(this.position.z));
|
||||
// TODO: Handle X and Z collisions.
|
||||
if (this.entityAABB.intersects(blockBoundingBox)) {
|
||||
const inersectionY = this.entityAABB.intersectionY(blockBoundingBox);
|
||||
this.position.add(0, inersectionY, 0);
|
||||
const intersectionY = this.entityAABB.intersectionY(blockBoundingBox);
|
||||
console.log(intersectionY);
|
||||
this.position.add(0, intersectionY, 0);
|
||||
this.motion.y = 0;
|
||||
this.onGround = true;
|
||||
}
|
||||
|
|
|
@ -24,13 +24,14 @@ export class EntityItem extends Entity {
|
|||
this.health = 5;
|
||||
}
|
||||
|
||||
onTick() {
|
||||
async onTick() {
|
||||
super.onTick();
|
||||
if (this.pickupDelay > 0) {
|
||||
this.pickupDelay--;
|
||||
} else {
|
||||
let playerCollided;
|
||||
if (playerCollided = this.collidesWithPlayer(this.entityAABB)) {
|
||||
let playerCollided = await this.collidesWithPlayer(this.entityAABB);
|
||||
if (playerCollided !== undefined) {
|
||||
console.log(playerCollided.username);
|
||||
playerCollided.inventory.addItemStack(this.itemStack);
|
||||
playerCollided.itemPickup(this, this.itemStack.size);
|
||||
if (this.itemStack.size <= 0) {
|
||||
|
|
|
@ -1,15 +1,8 @@
|
|||
import { IReader, IWriter } from "bufferstuff";
|
||||
import { Rotation } from "../Rotation";
|
||||
import Vec3 from "../Vec3";
|
||||
import { World } from "../World";
|
||||
import { Block } from "../blocks/Block";
|
||||
import { EntityStatus } from "../enums/EntityStatus";
|
||||
import { PacketAnimation } from "../packets/Animation";
|
||||
import { PacketEntityLook } from "../packets/EntityLook";
|
||||
import { PacketEntityLookRelativeMove } from "../packets/EntityLookRelativeMove";
|
||||
import { PacketEntityRelativeMove } from "../packets/EntityRelativeMove";
|
||||
import { PacketEntityStatus } from "../packets/EntityStatus";
|
||||
import { PacketEntityTeleport } from "../packets/EntityTeleport";
|
||||
import { Entity } from "./Entity";
|
||||
import { IEntity } from "./IEntity";
|
||||
|
||||
|
|
|
@ -35,6 +35,8 @@ export enum Packet {
|
|||
MultiBlockChange = 0x34,
|
||||
BlockChange = 0x035,
|
||||
|
||||
SoundEffect = 0x3D,
|
||||
|
||||
Entity = 0x1E,
|
||||
EntityRelativeMove = 0x1F,
|
||||
EntityLook = 0x20,
|
||||
|
|
10
server/enums/SoundEffects.ts
Normal file
10
server/enums/SoundEffects.ts
Normal file
|
@ -0,0 +1,10 @@
|
|||
export enum SoundEffects {
|
||||
CLICK2 = 1000,
|
||||
CLICK1 = 1001,
|
||||
BOW_FIRE = 1002,
|
||||
DOOR_TOGGLE = 1003,
|
||||
EXTINGUISH = 1004,
|
||||
RECORD_PLAY = 1005,
|
||||
SMOKE = 2000,
|
||||
BLOCK_BREAK = 2001
|
||||
}
|
|
@ -51,4 +51,6 @@ export class Item {
|
|||
static ironPickaxe = new Item(1).setMaxDamage(MaxUses.IRON).setName("Iron Pickaxe");
|
||||
static ironAxe = new Item(2).setMaxDamage(MaxUses.IRON).setName("Iron Axe");
|
||||
static ironSword = new Item(11).setMaxDamage(MaxUses.IRON).setName("Iron Sword");
|
||||
|
||||
static clay = new Item(81).setName("Clay");
|
||||
}
|
43
server/packets/SoundEffect.ts
Normal file
43
server/packets/SoundEffect.ts
Normal file
|
@ -0,0 +1,43 @@
|
|||
import { createWriter, IReader, Endian } from "bufferstuff";
|
||||
import { IPacket } from "./IPacket";
|
||||
import { Packet } from "../enums/Packet";
|
||||
import { SoundEffects } from "../enums/SoundEffects";
|
||||
|
||||
export class PacketSoundEffect implements IPacket {
|
||||
public packetId = Packet.SoundEffect;
|
||||
public effectId:SoundEffects;
|
||||
public x:number;
|
||||
public y:number;
|
||||
public z:number;
|
||||
public soundData:number;
|
||||
|
||||
public constructor(effectId:number, x:number, y:number, z:number, soundData:number) {
|
||||
if (typeof(effectId) === "number" && typeof(x) === "number" && typeof(y) === "number" && typeof(z) === "number" && typeof(soundData) === "number") {
|
||||
this.effectId = effectId;
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
this.soundData = soundData;
|
||||
} else {
|
||||
this.effectId = Number.MIN_VALUE;
|
||||
this.x = Number.MIN_VALUE;
|
||||
this.y = Number.MIN_VALUE;
|
||||
this.z = Number.MIN_VALUE;
|
||||
this.soundData = Number.MIN_VALUE;
|
||||
}
|
||||
}
|
||||
|
||||
public readData(reader:IReader) {
|
||||
this.effectId = reader.readInt();
|
||||
this.x = reader.readInt();
|
||||
this.y = reader.readByte();
|
||||
this.z = reader.readInt();
|
||||
this.soundData = reader.readInt();
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public writeData() {
|
||||
return createWriter(Endian.BE, 18).writeUByte(this.packetId).writeInt(this.effectId).writeInt(this.x).writeByte(this.y).writeInt(this.z).writeInt(this.soundData).toBuffer();
|
||||
}
|
||||
}
|
|
@ -8,10 +8,10 @@ export class PacketUseEntity implements IPacket {
|
|||
public targetId:number;
|
||||
public leftClick:boolean;
|
||||
|
||||
public constructor(userId:number, targetId:number, leftClick:boolean) {
|
||||
this.userId = userId;
|
||||
this.targetId = targetId;
|
||||
this.leftClick = leftClick;
|
||||
public constructor(userId?:number, targetId?:number, leftClick?:boolean) {
|
||||
this.userId = userId ?? Number.MIN_VALUE;
|
||||
this.targetId = targetId ?? Number.MIN_VALUE;
|
||||
this.leftClick = leftClick ?? false;
|
||||
}
|
||||
|
||||
public readData(reader:IReader) {
|
||||
|
|
Loading…
Reference in a new issue