Skip to content

Commit

Permalink
fix: support blocks that cancel fall damages
Browse files Browse the repository at this point in the history
Closes: #620
  • Loading branch information
Sceat committed Aug 11, 2023
1 parent a5a4e40 commit 5fa5845
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 28 deletions.
16 changes: 10 additions & 6 deletions src/player/damage.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { CATEGORY, play_sound } from '../sound.js'
import { PLAYER_ENTITY_ID } from '../settings.js'
import { compute_received_experience } from '../experience.js'
import Colors from '../colors.js'
import { can_receive_damage } from '../permissions.js'

const DAMAGE_INDICATORS_AMOUNT = 10
const DAMAGE_INDICATOR_TTL = 1200
Expand All @@ -31,12 +32,15 @@ export default {
observe({ events, dispatch, client, world, signal, get_state }) {
aiter(abortable(on(events, 'RECEIVE_DAMAGE', { signal }))).forEach(
([{ damage }]) => {
const { health } = get_state()
client.write('entity_status', {
entityId: PLAYER_ENTITY_ID,
entityStatus: health - damage > 0 ? 2 : 3, // Hurt Animation and Hurt Sound (sound not working)
})
dispatch('UPDATE_HEALTH', { health: health - damage })
const state = get_state()
if (can_receive_damage(state)) {
const { health } = state
client.write('entity_status', {
entityId: PLAYER_ENTITY_ID,
entityStatus: health - damage > 0 ? 2 : 3, // Hurt Animation and Hurt Sound (sound not working)
})
dispatch('UPDATE_HEALTH', { health: health - damage })
}
},
)

Expand Down
89 changes: 67 additions & 22 deletions src/player/fall_damage.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,46 +3,91 @@ import { on } from 'events'
import { aiter } from 'iterator-helper'

import { abortable } from '../iterator.js'
import { block_position } from '../position.js'
import { get_block, same_position } from '../chunk.js'

const CANCELLING_FALL_DAMAGE_BLOCKS = [
// guess falling on top of a ladder is fine
'ladder',
'slime_block',
'vine',
'water',
'honey',
'hay_block',
]

export default {
/** @type {import('../context.js').Observer} */
observe({ events, dispatch, signal }) {
observe({ events, dispatch, signal, world }) {
aiter(abortable(on(events, 'STATE_UPDATED', { signal })))
.map(
([
{
position: { y, onGround },
teleport,
},
]) => ({ y, onGround, teleport }),
)
.map(([{ position, teleport }]) => ({ position, teleport }))
.reduce(
(
{ highest_y, was_on_ground, last_teleport },
{ y, onGround, teleport },
async (
{ highest_y, was_on_ground, last_teleport, last_block_position },
{ position, teleport },
) => {
const current_block_position = block_position(position)
const { y, onGround } = position
const result = {
highest_y,
was_on_ground: onGround,
last_teleport: teleport,
last_block_position: current_block_position,
}

// if the player moved and is not on the ground,
// we need to check if their current block is a block that cancels fall damage (like water) to reset the highest_y
if (!same_position(current_block_position, last_block_position)) {
if (!onGround) {
const block = await get_block(world, current_block_position)
const just_teleported = !teleport && last_teleport
const can_reset_y =
just_teleported ||
CANCELLING_FALL_DAMAGE_BLOCKS.includes(block.name)

return {
...result,
highest_y: can_reset_y ? y : Math.max(highest_y, y),
}
}
}

// if the player reached the ground from the air, we need to check if they fell from a high enough place to inflict damage
if (!was_on_ground && onGround) {
const block_below = await get_block(world, {
...current_block_position,
y: current_block_position.y - 1,
})
const block = await get_block(world, current_block_position)

if (
CANCELLING_FALL_DAMAGE_BLOCKS.includes(block_below.name) ||
CANCELLING_FALL_DAMAGE_BLOCKS.includes(block.name)
)
return {
...result,
highest_y: y,
}

const fall_distance = highest_y - y
const raw_damage = fall_distance / 2 - 1.5
const damage = Math.round(raw_damage * 2) / 2

if (damage > 0) events.emit('RECEIVE_DAMAGE', { damage })
return {
...result,
highest_y: y,
was_on_ground: true,
last_teleport: teleport,
}
}

const reset_y = last_teleport !== teleport && teleport === null // Teleport just ended

return {
highest_y: reset_y ? y : Math.max(highest_y, y),
was_on_ground: onGround,
last_teleport: teleport,
}
return result
},
{
highest_y: 0,
was_on_ground: true,
last_teleport: null,
last_block_position: null,
},
{ highest_y: 0, was_on_ground: true, last_teleport: null },
)
},
}

0 comments on commit 5fa5845

Please sign in to comment.