Implement fall damage & fix damage in general
This commit is contained in:
parent
a3c865bd9f
commit
522916ecaa
13 changed files with 148 additions and 22 deletions
|
@ -28,6 +28,7 @@ export class MPClient {
|
|||
private readonly socket:Socket;
|
||||
public entity:Player;
|
||||
private inventory:Inventory;
|
||||
private dimension:number;
|
||||
|
||||
private holdingIndex:number = 36; // First hotbar slot.
|
||||
private diggingAt:Vec3;
|
||||
|
@ -37,6 +38,7 @@ export class MPClient {
|
|||
this.socket = socket;
|
||||
this.entity = entity;
|
||||
this.inventory = entity.inventory;
|
||||
this.dimension = 0;
|
||||
|
||||
this.diggingAt = new Vec3();
|
||||
}
|
||||
|
@ -69,6 +71,7 @@ export class MPClient {
|
|||
|
||||
switch (packetId) {
|
||||
case Packet.Chat: this.handleChat(new PacketChat().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;
|
||||
case Packet.PlayerLook: this.handlePacketPlayerLook(new PacketPlayerLook().readData(reader)); break;
|
||||
|
@ -122,24 +125,35 @@ export class MPClient {
|
|||
this.mcServer.sendToAllClients(packet.writeData());
|
||||
}
|
||||
|
||||
private handlePacketRespawn(packet:PacketRespawn) {
|
||||
if (this.entity.health > 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private handlePacketPlayer(packet:PacketPlayer) {
|
||||
this.entity.onGround = packet.onGround;
|
||||
}
|
||||
|
||||
private handlePacketPlayerPosition(packet:PacketPlayerPosition) {
|
||||
this.entity.onGround = packet.onGround;
|
||||
this.entity.position.set(packet.x, packet.y, packet.z);
|
||||
}
|
||||
|
||||
private handlePacketPlayerLook(packet:PacketPlayerLook) {
|
||||
this.entity.onGround = packet.onGround;
|
||||
this.entity.rotation.set(packet.yaw, packet.pitch);
|
||||
}
|
||||
|
||||
private handlePacketPlayerPositionLook(packet:PacketPlayerPositionLook) {
|
||||
this.entity.onGround = packet.onGround;
|
||||
this.entity.position.set(packet.x, packet.y, packet.z);
|
||||
this.entity.rotation.set(packet.yaw, packet.pitch);
|
||||
}
|
||||
|
||||
private handlePacketPlayerDigging(packet:PacketPlayerDigging) {
|
||||
console.log(packet.status);
|
||||
|
||||
// Special drop item case
|
||||
if (packet.status === 4) {
|
||||
// TODO: Handle dropping items
|
||||
|
@ -156,7 +170,6 @@ export class MPClient {
|
|||
if ((brokenBlockId = this.entity.world.getBlockId(this.diggingAt.x, this.diggingAt.y, this.diggingAt.z)) != 0) {
|
||||
const metadata = this.entity.world.getBlockMetadata(this.diggingAt.x, this.diggingAt.y, this.diggingAt.z);
|
||||
this.entity.world.setBlockWithNotify(this.diggingAt.x, this.diggingAt.y, this.diggingAt.z, 0);
|
||||
console.log("Metadata: ", metadata);
|
||||
this.inventory.addItemStack(new ItemStack(Block.blockBehaviours[brokenBlockId].droppedItem(brokenBlockId), 1, metadata));
|
||||
this.send(new PacketWindowItems(0, this.inventory.getInventorySize(), this.inventory.constructInventoryPayload()).writeData());
|
||||
}
|
||||
|
|
|
@ -2,12 +2,15 @@ import { World } from "../World";
|
|||
import { BlockBehaviour } from "./BlockBehaviour";
|
||||
import { BlockBehaviourFlower } from "./BlockBehaviourFlower";
|
||||
import { BlockBehaviourGrass } from "./BlockBehaviourGrass";
|
||||
import { BlockBehaviourStone } from "./BlockBehaviourStone";
|
||||
import { IBlockBehaviour } from "./IBlockBehaviour";
|
||||
|
||||
abstract class Behaviour {
|
||||
public static base = new BlockBehaviour();
|
||||
|
||||
public static stone = new BlockBehaviourStone();
|
||||
public static grass = new BlockBehaviourGrass();
|
||||
|
||||
public static flower = new BlockBehaviourFlower();
|
||||
}
|
||||
|
||||
|
@ -75,9 +78,10 @@ export class Block {
|
|||
}
|
||||
|
||||
// Define statics here
|
||||
static readonly stone = new Block(1).setBlockName("Stone");
|
||||
static readonly stone = new Block(1).setBehaviour(Behaviour.stone).setBlockName("Stone");
|
||||
static readonly grass = new Block(2).setBehaviour(Behaviour.grass).setBlockName("Grass");
|
||||
static readonly dirt = new Block(3).setBlockName("Dirt");
|
||||
static readonly cobblestone = new Block(4).setBlockName("Cobblestone");
|
||||
|
||||
static readonly bedrock = new Block(7).setBlockName("Bedrock");
|
||||
|
||||
|
|
8
server/blocks/BlockBehaviourStone.ts
Normal file
8
server/blocks/BlockBehaviourStone.ts
Normal file
|
@ -0,0 +1,8 @@
|
|||
import { Block } from "./Block";
|
||||
import { BlockBehaviour } from "./BlockBehaviour";
|
||||
|
||||
export class BlockBehaviourStone extends BlockBehaviour {
|
||||
public droppedItem(blockId:number) {
|
||||
return Block.cobblestone.blockId;
|
||||
}
|
||||
}
|
|
@ -22,6 +22,7 @@ export class Entity implements IEntity {
|
|||
public lastPosition:Vec3;
|
||||
public absPosition:Vec3;
|
||||
public lastAbsPosition:Vec3;
|
||||
public motion:Vec3;
|
||||
|
||||
public rotation:Rotation;
|
||||
public lastRotation:Rotation;
|
||||
|
@ -35,6 +36,9 @@ export class Entity implements IEntity {
|
|||
public isDead:boolean;
|
||||
|
||||
public fire:number;
|
||||
public fallDistance:number;
|
||||
|
||||
public onGround:boolean;
|
||||
|
||||
public chunk:Chunk;
|
||||
|
||||
|
@ -47,7 +51,8 @@ export class Entity implements IEntity {
|
|||
public constructor(world:World) {
|
||||
this.entityId = Entity.nextEntityId++;
|
||||
|
||||
this.fire = 0;
|
||||
this.fire = this.fallDistance = 0;
|
||||
this.onGround = false;
|
||||
|
||||
this.world = world;
|
||||
|
||||
|
@ -55,6 +60,7 @@ export class Entity implements IEntity {
|
|||
this.lastPosition = new Vec3();
|
||||
this.absPosition = new Vec3();
|
||||
this.lastAbsPosition = new Vec3();
|
||||
this.motion = new Vec3();
|
||||
|
||||
this.rotation = new Rotation();
|
||||
this.lastRotation = new Rotation();
|
||||
|
@ -77,7 +83,7 @@ export class Entity implements IEntity {
|
|||
}
|
||||
|
||||
sendToAllNearby(buffer:Buffer) {
|
||||
this.world.sendToNearbyClients(this, buffer);
|
||||
this.world.sendToNearbyAllNearbyClients(this, buffer);
|
||||
}
|
||||
|
||||
updateMetadata() {
|
||||
|
@ -169,9 +175,26 @@ export class Entity implements IEntity {
|
|||
}
|
||||
}
|
||||
|
||||
fall(distance:number) {
|
||||
// TODO: Entity falling mount transfer
|
||||
}
|
||||
|
||||
updateFalling(distance:number) {
|
||||
if (this.onGround) {
|
||||
if (this.fallDistance > 0)
|
||||
{
|
||||
this.fall(this.fallDistance);
|
||||
this.fallDistance = 0;
|
||||
}
|
||||
} else if (distance < 0) {
|
||||
this.fallDistance -= distance;
|
||||
}
|
||||
}
|
||||
|
||||
onTick() {
|
||||
this.updateMetadata();
|
||||
this.updateEntityChunk();
|
||||
this.updateFalling(this.motion.y);
|
||||
|
||||
if (this.fire > 0) {
|
||||
if (this.fire % 20 === 0) {
|
||||
|
|
|
@ -2,6 +2,7 @@ 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";
|
||||
|
@ -12,17 +13,18 @@ import { Entity } from "./Entity";
|
|||
import { IEntity } from "./IEntity";
|
||||
|
||||
export class EntityLiving extends Entity {
|
||||
public onGround:boolean;
|
||||
public fallDistance:number;
|
||||
public timeInWater:number;
|
||||
public headHeight:number;
|
||||
public lastHealth:number;
|
||||
|
||||
public constructor(world:World) {
|
||||
super(world);
|
||||
|
||||
this.fallDistance = this.timeInWater = 0;
|
||||
this.onGround = true;
|
||||
this.headHeight = 1.62;
|
||||
|
||||
this.lastHealth = this.health;
|
||||
}
|
||||
|
||||
damageFrom(damage:number, entity?:IEntity) {
|
||||
|
@ -31,14 +33,25 @@ export class EntityLiving extends Entity {
|
|||
}
|
||||
super.damageFrom(damage, entity);
|
||||
|
||||
// Send Damage Animation packet
|
||||
this.sendToAllNearby(new PacketEntityStatus(this.entityId, 2).writeData());
|
||||
// Send Damage Animation packet or death packet
|
||||
if (this.health === 0) {
|
||||
this.sendToAllNearby(new PacketEntityStatus(this.entityId, EntityStatus.Dead).writeData());
|
||||
} else {
|
||||
this.sendToAllNearby(new PacketEntityStatus(this.entityId, EntityStatus.Hurt).writeData());
|
||||
}
|
||||
}
|
||||
|
||||
isInWater() {
|
||||
return this.world.getChunkBlockId(this.chunk, this.position.x, this.position.y + this.headHeight, this.position.z) === Block.waterStill.blockId;
|
||||
}
|
||||
|
||||
fall(distance:number) {
|
||||
const adjustedFallDistance = Math.ceil(distance - 3);
|
||||
if (adjustedFallDistance > 0) {
|
||||
this.damageFrom(adjustedFallDistance);
|
||||
}
|
||||
}
|
||||
|
||||
onTick() {
|
||||
super.onTick();
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ import { Block } from "../blocks/Block";
|
|||
import PlayerInventory from "../inventories/PlayerInventory";
|
||||
import { Item } from "../items/Item";
|
||||
|
||||
const CHUNK_LOAD_RANGE = 5;
|
||||
const CHUNK_LOAD_RANGE = 15;
|
||||
|
||||
export class Player extends EntityLiving {
|
||||
public username:string;
|
||||
|
@ -23,8 +23,6 @@ export class Player extends EntityLiving {
|
|||
public mpClient?:MPClient;
|
||||
public inventory:PlayerInventory;
|
||||
|
||||
private lastHealth:number;
|
||||
|
||||
public constructor(server:MinecraftServer, world:World, username:string) {
|
||||
super(world);
|
||||
this.server = server;
|
||||
|
@ -42,8 +40,6 @@ export class Player extends EntityLiving {
|
|||
|
||||
this.username = username;
|
||||
this.position.set(8, 64, 8);
|
||||
|
||||
this.lastHealth = this.health;
|
||||
}
|
||||
|
||||
public forceUpdatePlayerChunks() {
|
||||
|
@ -97,11 +93,14 @@ export class Player extends EntityLiving {
|
|||
public onTick() {
|
||||
this.updatePlayerChunks();
|
||||
|
||||
// Calculate player motion
|
||||
this.motion.set(this.position.x - this.lastPosition.x, this.position.y - this.lastPosition.y, this.position.z - this.lastPosition.z);
|
||||
|
||||
super.onTick();
|
||||
|
||||
if (this.health != this.lastHealth) {
|
||||
this.lastHealth = this.health;
|
||||
this.mpClient?.send(new PacketUpdateHealth(this.health).writeData());
|
||||
}
|
||||
|
||||
super.onTick();
|
||||
}
|
||||
}
|
8
server/enums/EntityStatus.ts
Normal file
8
server/enums/EntityStatus.ts
Normal file
|
@ -0,0 +1,8 @@
|
|||
export enum EntityStatus {
|
||||
Unknown0,
|
||||
Unknown1,
|
||||
Hurt,
|
||||
Dead,
|
||||
Unknown4,
|
||||
Unknown5
|
||||
}
|
7
server/enums/MaxUses.ts
Normal file
7
server/enums/MaxUses.ts
Normal file
|
@ -0,0 +1,7 @@
|
|||
export enum MaxUses {
|
||||
GOLD = 32,
|
||||
WOOD = 59,
|
||||
STONE = 131,
|
||||
IRON = 250,
|
||||
DIAMOND = 1561
|
||||
}
|
|
@ -1,13 +1,18 @@
|
|||
import { Block } from "../blocks/Block";
|
||||
import { IEntity } from "../entities/IEntity";
|
||||
import { Player } from "../entities/Player";
|
||||
import { Item } from "../items/Item";
|
||||
|
||||
export class ItemStack {
|
||||
public readonly itemID:number;
|
||||
public readonly isBlock:boolean;
|
||||
public readonly maxSize:number;
|
||||
public size:number;
|
||||
public damage:number;
|
||||
|
||||
private readonly maxSize:number;
|
||||
private readonly maxDamage:number;
|
||||
private readonly canBeDamaged:boolean;
|
||||
|
||||
public constructor(blockOrItemOrItemID:Block|Item|number, size?:number, damage?:number) {
|
||||
if (blockOrItemOrItemID instanceof Block && size === undefined && damage === undefined) {
|
||||
this.itemID = blockOrItemOrItemID.blockId;
|
||||
|
@ -50,7 +55,9 @@ export class ItemStack {
|
|||
}
|
||||
|
||||
this.isBlock = this.itemID < 256;
|
||||
this.maxSize = !this.isBlock ? Item.getByShiftedItemId(this.itemID).maxStackSize : 64;
|
||||
this.maxSize = this.isBlock ? 64 : Item.getByShiftedItemId(this.itemID).maxStackSize;
|
||||
this.maxDamage = this.isBlock ? 0 : Item.getByShiftedItemId(this.itemID).maxDamage;
|
||||
this.canBeDamaged = this.maxDamage > 0;
|
||||
}
|
||||
|
||||
public insert(itemStack:ItemStack) {
|
||||
|
@ -71,6 +78,21 @@ export class ItemStack {
|
|||
}
|
||||
}
|
||||
|
||||
public damageItem(damageAmount:number, entity:IEntity) {
|
||||
if (!this.canBeDamaged) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.damage += damageAmount;
|
||||
if (this.damage > this.maxDamage) {
|
||||
this.size--;
|
||||
if (this.size < 0) {
|
||||
this.size = 0;
|
||||
}
|
||||
this.damage = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public get spaceAvaliable() {
|
||||
// Stack size check for Item(s) and Block(s).
|
||||
return Math.max(this.maxSize - this.size, 0);
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
import { EntityLiving } from "../entities/EntityLiving";
|
||||
import { ItemStack } from "../inventories/ItemStack";
|
||||
|
||||
export interface IItemBehaviour {
|
||||
|
||||
}
|
|
@ -1,12 +1,16 @@
|
|||
import { MaxUses } from "../enums/MaxUses";
|
||||
|
||||
export class Item {
|
||||
public static items:Array<Item> = new Array<Item>();
|
||||
|
||||
public maxStackSize:number;
|
||||
public maxDamage:number;
|
||||
public shiftedItemID:number;
|
||||
public name:string;
|
||||
|
||||
public constructor(itemID:number) {
|
||||
this.shiftedItemID = 256 + itemID;
|
||||
this.maxDamage = 0;
|
||||
this.maxStackSize = 64;
|
||||
this.name = "UNNAMED";
|
||||
|
||||
|
@ -22,6 +26,16 @@ export class Item {
|
|||
return this;
|
||||
}
|
||||
|
||||
public getMaxDamage() {
|
||||
return this.maxDamage;
|
||||
}
|
||||
|
||||
public setMaxDamage(value:number) {
|
||||
this.maxDamage = value;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public setName(name:string) {
|
||||
this.name = name;
|
||||
|
||||
|
@ -33,8 +47,8 @@ export class Item {
|
|||
}
|
||||
|
||||
// Define statics here
|
||||
static ironShovel = new Item(0).setName("Iron Shovel");
|
||||
static ironPickaxe = new Item(1).setName("Iron Pickaxe");
|
||||
static ironAxe = new Item(2).setName("Iron Axe");
|
||||
static ironSword = new Item(11).setName("Iron Sword");
|
||||
static ironShovel = new Item(0).setMaxDamage(MaxUses.IRON).setName("Iron Shovel");
|
||||
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");
|
||||
}
|
7
server/items/ItemBehaviour.ts
Normal file
7
server/items/ItemBehaviour.ts
Normal file
|
@ -0,0 +1,7 @@
|
|||
import { EntityLiving } from "../entities/EntityLiving";
|
||||
import { ItemStack } from "../inventories/ItemStack";
|
||||
import { IItemBehaviour } from "./IItemBehaviour";
|
||||
|
||||
export default class ItemBehaviour implements IItemBehaviour {
|
||||
|
||||
}
|
5
server/items/ItemBehaviourTool.ts
Normal file
5
server/items/ItemBehaviourTool.ts
Normal file
|
@ -0,0 +1,5 @@
|
|||
import ItemBehaviour from "./ItemBehaviour";
|
||||
|
||||
export default class ItemBehaviourTool extends ItemBehaviour {
|
||||
|
||||
}
|
Loading…
Reference in a new issue