Skip to content

Commit

Permalink
Merge pull request #61 from Felipe-Devr/develop
Browse files Browse the repository at this point in the history
feat: added experience component, implement ActorDamageCause enum, reset of hunger, exhaustion, experience and saturation on death
  • Loading branch information
PMK744 authored Jul 16, 2024
2 parents be11d0e + 43b8f39 commit a28300b
Show file tree
Hide file tree
Showing 15 changed files with 294 additions and 69 deletions.
40 changes: 40 additions & 0 deletions packages/protocol/src/enums/actor-damage-cause.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
enum ActorDamageCause {
None = -1,
Override,
Contact,
EntityAttack,
Projectile,
Suffocation,
Fall,
Fire,
FireTick,
Lava,
Drowning,
BlockExplosion,
EntityExplosion,
Void,
SelfDestruct,
Magic,
Wither,
Starve,
Anvil,
Thorns,
FallingBlock,
Piston,
FlyIntoWall,
Magma,
Fireworks,
Lightning,
Charging,
Temperature,
Freezing,
Stalactite,
Stalagmite,
RamAttack,
SonicBoom,
Campfire,
SoulCampfire,
All
}

export { ActorDamageCause };
1 change: 1 addition & 0 deletions packages/protocol/src/enums/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,4 @@ export * from "./inventory-right-tab";
export * from "./actor-data-id";
export * from "./actor-flag";
export * from "./actor-data-type";
export * from "./actor-damage-cause";
8 changes: 6 additions & 2 deletions packages/world/src/components/entity/attribute/health.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {
ActorDamageCause,
ActorEventIds,
ActorEventPacket,
AttributeName,
Expand Down Expand Up @@ -50,7 +51,7 @@ class EntityHealthComponent extends EntityAttributeComponent {
* Applies damage to the entity.
* @param damage The amount of damage to apply to the entity.
*/
public applyDamage(damage: number): void {
public applyDamage(damage: number, cause?: ActorDamageCause): void {
// Decrease the health of the entity
this.decreaseValue(damage);

Expand All @@ -60,7 +61,7 @@ class EntityHealthComponent extends EntityAttributeComponent {
// Assign the values to the packet
packet.actorRuntimeId = this.entity.runtime;
packet.eventId = ActorEventIds.HURT_ANIMATION;
packet.eventData = -1;
packet.eventData = cause ?? ActorDamageCause.None;
this.entity.dimension.broadcast(packet);

// Check if the entity is dead
Expand Down Expand Up @@ -108,6 +109,9 @@ class EntityHealthComponent extends EntityAttributeComponent {
player: Player,
type: ItemUseOnEntityInventoryTransactionType
): void {
/**
* TODO: Calculate the damage based on the player, projectile , etc.
*/
// Check if the player is attacking the entity
if (type !== ItemUseOnEntityInventoryTransactionType.Attack) return;

Expand Down
50 changes: 50 additions & 0 deletions packages/world/src/components/player/attribute/experience-level.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { AttributeName } from "@serenityjs/protocol";

import { EntityAttributeComponent } from "../../entity/attribute/attribute";

import type { Player } from "../../../player";

class PlayerExperienceLevelComponent extends EntityAttributeComponent {
public static readonly identifier = AttributeName.PlayerLevel;

public readonly effectiveMin: number = 0;
/**
* Maximum XP Level visible?
*/
public readonly effectiveMax: number = 24_792;
public readonly defaultValue: number = 0;

public constructor(player: Player) {
super(player, PlayerExperienceLevelComponent.identifier);
this.setCurrentValue(this.defaultValue, false);
}

public toExperience(): number {
let totalExperience = 0;
switch (true) {
case this.level <= 16: {
totalExperience = this.level ** 2 + 6 * this.level;
break;
}
case this.level > 16 && this.level <= 31: {
totalExperience = 2.5 * this.level ** 2 - 40.5 * this.level + 360;
break;
}
case this.level > 31: {
totalExperience = 4.5 * this.level ** 2 - 162.5 * this.level + 2220;
break;
}
}
return totalExperience;
}

public set level(newExperienceLevel: number) {
this.setCurrentValue(newExperienceLevel, true);
}

public get level(): number {
return this.getCurrentValue();
}
}

export { PlayerExperienceLevelComponent };
67 changes: 67 additions & 0 deletions packages/world/src/components/player/attribute/experience.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { AttributeName } from "@serenityjs/protocol";

import { EntityAttributeComponent } from "../../entity/attribute/attribute";

import type { Player } from "../../../player";

class PlayerExperienceComponent extends EntityAttributeComponent {
public static readonly identifier = AttributeName.PlayerExperience;
public readonly effectiveMin: number = 0;
/**
* Infinity because it converts automatically to xp levels
*/
public readonly effectiveMax: number = Infinity;
public readonly defaultValue: number = 0;

public constructor(player: Player) {
super(player, PlayerExperienceComponent.identifier);
this.setCurrentValue(this.defaultValue, false);
}

public addExperience(experienceAmount: number) {
if (!this.entity.isPlayer()) return;
const experienceLevelComponent = this.entity.getComponent(
"minecraft:player.level"
);

// ? Add the saved experience.
let newXp = experienceAmount + this.experience;

// ? While the xp value is greather or equal to the required xp for the next level, do level up

while (newXp >= this.entity.getNextLevelXp()) {
newXp -= this.entity.getNextLevelXp();
experienceLevelComponent.increaseValue(1);
}
// ? Remove the current xp and add the remaining xp value

this.setCurrentValue(newXp, true);
}

public removeExperience(experienceAmount: number) {
if (!this.entity.isPlayer()) return;
const experienceLevelComponent = this.entity.getComponent(
"minecraft:player.level"
);

// ? Add the saved experience.
let newXp = experienceAmount - this.experience;

// ? While the xp value is greather or equal to the previus level, do level down

while (newXp >= this.entity.getNextLevelXp(this.entity.level - 1)) {
newXp -= this.entity.getNextLevelXp(this.entity.level - 1);
experienceLevelComponent.decreaseValue(1);
}

// ? Remove the current xp and add the remaining xp value

this.setCurrentValue(newXp, true);
}

public get experience(): number {
return this.getCurrentValue();
}
}

export { PlayerExperienceComponent };
12 changes: 3 additions & 9 deletions packages/world/src/components/player/attribute/hunger.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {
ActorDamageCause,
ActorEventIds,
ActorEventPacket,
AttributeName,
Expand Down Expand Up @@ -53,15 +54,8 @@ class PlayerHungerComponent extends EntityAttributeComponent {
entityHealthComponent.increaseValue(1);
this.exhaust(6);
} else if (this.food <= 0) {
// Create depending on difficulty
// ! Temporal Implementation until damage is fully implemented
const packet = new ActorEventPacket();
packet.actorRuntimeId = this.entity.runtime;
packet.eventId = ActorEventIds.HURT_ANIMATION;
packet.eventData = -1;

this.entity.dimension.broadcast(packet);
entityHealthComponent.decreaseValue(1);
// add starve depending on difficulty
entityHealthComponent.applyDamage(1, ActorDamageCause.Starve);
}
}
if (this.food <= 6) {
Expand Down
2 changes: 2 additions & 0 deletions packages/world/src/components/player/attribute/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
export * from "./hunger";
export * from "./exhaustion";
export * from "./saturation";
export * from "./experience-level";
export * from "./experience";
16 changes: 2 additions & 14 deletions packages/world/src/effect/fatal-poison.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import {
ActorEventIds,
ActorEventPacket,
ActorDamageCause,
Color,
EffectType,
Gamemode
Expand All @@ -24,20 +23,9 @@ class FatalPoisonEffect<T extends Entity> extends Effect {

if (Number(entity.dimension.world.currentTick) % ticksPerSecond != 0)
return;
const entityHealth = entity.getComponent("minecraft:health");

if (entity.isPlayer() && entity.gamemode == Gamemode.Creative) return;
if (!entity.isAlive) {
this.duration = 0;
return;
}
const packet = new ActorEventPacket();
packet.actorRuntimeId = entity.runtime;
packet.eventId = ActorEventIds.HURT_ANIMATION;
packet.eventData = -1;

entity.dimension.broadcast(packet);
entityHealth.decreaseValue(1);
entity.applyDamage(1, ActorDamageCause.Magic);
}

public onAdd?(_entity: T): void {}
Expand Down
2 changes: 1 addition & 1 deletion packages/world/src/effect/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ export * from "./slowness";
export * from "./levitation";
export * from "./instant-health";
export * from "./instant-damage";
/* export * from "./strength"; */

export * from "./invisibility";
export * from "./poison";
export * from "./wither";
Expand Down
17 changes: 2 additions & 15 deletions packages/world/src/effect/instant-damage.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,4 @@
import {
ActorEventIds,
ActorEventPacket,
EffectType,
Gamemode
} from "@serenityjs/protocol";
import { ActorDamageCause, EffectType, Gamemode } from "@serenityjs/protocol";

import { Effect } from "./effect";

Expand All @@ -16,20 +11,12 @@ class InstantDamage<T extends Entity> extends Effect {
public onTick?(_entity: T): void {}

public onAdd?(entity: T): void {
const entityHealth = entity.getComponent("minecraft:health");

// TODO: Undead check for healing
//if (entity)

if (entity.isPlayer() && entity.gamemode == Gamemode.Creative) return;

const packet = new ActorEventPacket();
packet.actorRuntimeId = entity.runtime;
packet.eventId = ActorEventIds.HURT_ANIMATION;
packet.eventData = -1;

entity.dimension.broadcast(packet);
entityHealth.decreaseValue(3 * 2 ** this.amplifier);
entity.applyDamage(3 * 2 ** this.amplifier, ActorDamageCause.Magic);
}

public onRemove?(_entity: T): void {}
Expand Down
11 changes: 2 additions & 9 deletions packages/world/src/effect/poison.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import {
ActorEventIds,
ActorEventPacket,
ActorDamageCause,
Color,
EffectType,
Gamemode
Expand All @@ -27,13 +26,7 @@ class PoisonEffect<T extends Entity> extends Effect {
const entityHealth = entity.getComponent("minecraft:health");

if (entityHealth.getCurrentValue() <= 1) return;
const packet = new ActorEventPacket();
packet.actorRuntimeId = entity.runtime;
packet.eventId = ActorEventIds.HURT_ANIMATION;
packet.eventData = -1;

entity.dimension.broadcast(packet);
entityHealth.decreaseValue(1);
entity.applyDamage(1, ActorDamageCause.Magic);
}

public onAdd?(entity: T): void;
Expand Down
16 changes: 2 additions & 14 deletions packages/world/src/effect/wither.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import {
ActorEventIds,
ActorEventPacket,
ActorDamageCause,
Color,
EffectType,
Gamemode
Expand All @@ -21,19 +20,8 @@ class WitherEffect<T extends Entity> extends Effect {
if (Number(entity.dimension.world.currentTick) % ticksPerSecond != 0)
return;
if (entity.isPlayer() && entity.gamemode == Gamemode.Creative) return;
const entityHealth = entity.getComponent("minecraft:health");

if (!entity.isAlive) {
this.duration = 0;
return;
}
const packet = new ActorEventPacket();
packet.actorRuntimeId = entity.runtime;
packet.eventId = ActorEventIds.HURT_ANIMATION;
packet.eventData = -1;

entity.dimension.broadcast(packet);
entityHealth.decreaseValue(1);
entity.applyDamage(1, ActorDamageCause.Magic);
}

public onAdd?(entity: T): void;
Expand Down
18 changes: 15 additions & 3 deletions packages/world/src/entity/entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ import {
Vector3f,
PropertySyncData,
type DataItem,
type ActorFlag
type ActorFlag,
type ActorDamageCause
} from "@serenityjs/protocol";
import { EntityIdentifier, EntityType } from "@serenityjs/entity";
import { CommandExecutionState, type CommandResult } from "@serenityjs/command";
Expand Down Expand Up @@ -379,6 +380,17 @@ class Entity {
}
}

// Check if the entity has the effects component
if (this.hasComponent("minecraft:effects")) {
// Get the component
const effects = this.getComponent("minecraft:effects");

// Remove every effect of the player
for (const effectType of effects.effects.keys()) {
effects.remove(effectType);
}
}

// Check if the entity has a health component
if (this.hasComponent("minecraft:health")) {
// Get the health component
Expand Down Expand Up @@ -686,7 +698,7 @@ class Entity {
* @note This method is dependant on the entity having a `minecraft:health` component, if not will result in an `error`.
* @param damage The amount of damage to apply to the entity.
*/
public applyDamage(damage: number): void {
public applyDamage(damage: number, damageCause?: ActorDamageCause): void {
// Check if the entity has a health component
if (!this.hasComponent("minecraft:health"))
throw new Error("The entity does not have a health component.");
Expand All @@ -695,7 +707,7 @@ class Entity {
const health = this.getComponent("minecraft:health");

// Apply the damage to the entity
health.applyDamage(damage);
health.applyDamage(damage, damageCause);
}

/**
Expand Down
Loading

0 comments on commit a28300b

Please sign in to comment.