Implement fall damage & fix damage in general

This commit is contained in:
Holly Stubbs 2023-11-05 00:55:23 +00:00
parent a3c865bd9f
commit 522916ecaa
Signed by: tgpholly
GPG key ID: B8583C4B7D18119E
13 changed files with 148 additions and 22 deletions

View file

@ -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());
}

View file

@ -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");

View 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;
}
}

View file

@ -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) {

View file

@ -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();

View file

@ -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();
}
}

View file

@ -0,0 +1,8 @@
export enum EntityStatus {
Unknown0,
Unknown1,
Hurt,
Dead,
Unknown4,
Unknown5
}

7
server/enums/MaxUses.ts Normal file
View file

@ -0,0 +1,7 @@
export enum MaxUses {
GOLD = 32,
WOOD = 59,
STONE = 131,
IRON = 250,
DIAMOND = 1561
}

View file

@ -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);

View file

@ -1,3 +1,6 @@
import { EntityLiving } from "../entities/EntityLiving";
import { ItemStack } from "../inventories/ItemStack";
export interface IItemBehaviour {
}

View file

@ -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");
}

View 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 {
}

View file

@ -0,0 +1,5 @@
import ItemBehaviour from "./ItemBehaviour";
export default class ItemBehaviourTool extends ItemBehaviour {
}