commit - 3151f0b6f261ceff3802a7b6da6b3b2d10c3a4fa
commit + 8011a1adfdcb753f310eaa0f5f116df31f21c0af
blob - 8be34020f31f5313efa769993940532b70e3a292
blob + d9bf6274667881d202903ac5a0e2dfc0f0bf6f47
--- src/crafting.c
+++ src/crafting.c
return;
}
break;
+ // Boot recipes
+ case I_leather:
+ case I_iron_ingot:
+ case I_gold_ingot:
+ case I_diamond:
+ case I_netherite_ingot:
+ if (
+ first_col == 0 && first_row < 2 &&
+ player->craft_items[first + 2] == first_item &&
+ player->craft_items[first + 3] == first_item &&
+ player->craft_items[first + 5] == first_item
+ ) {
+ if (first_item == I_leather) *item = I_leather_boots;
+ else if (first_item == I_iron_ingot) *item = I_iron_boots;
+ else if (first_item == I_gold_ingot) *item = I_golden_boots;
+ else if (first_item == I_diamond) *item = I_diamond_boots;
+ else if (first_item == I_netherite_ingot) *item = I_netherite_boots;
+ *count = 1;
+ return;
+ }
+ break;
default: break;
}
*count = 1;
return;
}
+ if (
+ first_item == I_oak_planks ||
+ first_item == I_cobblestone
+ ) break;
+ case I_leather:
+ // Helmet recipes
+ if (
+ first_col == 0 && first_row < 2 &&
+ player->craft_items[first + 1] == first_item &&
+ player->craft_items[first + 2] == first_item &&
+ player->craft_items[first + 3] == first_item &&
+ player->craft_items[first + 5] == first_item
+ ) {
+ if (first_item == I_leather) *item = I_leather_helmet;
+ else if (first_item == I_iron_ingot) *item = I_iron_helmet;
+ else if (first_item == I_gold_ingot) *item = I_golden_helmet;
+ else if (first_item == I_diamond) *item = I_diamond_helmet;
+ else if (first_item == I_netherite_ingot) *item = I_netherite_helmet;
+ *count = 1;
+ return;
+ }
break;
default: break;
break;
case 7:
+ // Legging recipes
+ if (identical && player->craft_items[4] == 0 && player->craft_items[7] == 0) {
+ switch (first_item) {
+ case I_leather: *item = I_leather_leggings; *count = 1; return;
+ case I_iron_ingot: *item = I_iron_leggings; *count = 1; return;
+ case I_gold_ingot: *item = I_golden_leggings; *count = 1; return;
+ case I_diamond: *item = I_diamond_leggings; *count = 1; return;
+ case I_netherite_ingot: *item = I_netherite_leggings; *count = 1; return;
+ default: break;
+ }
+ }
switch (first_item) {
case I_oak_slab:
if (
break;
case 8:
- if (identical && player->craft_items[first + 4] == 0) {
- switch (first_item) {
- case I_cobblestone: *item = I_furnace; *count = 1; return;
- #ifdef ALLOW_CHESTS
- case I_oak_planks: *item = I_chest; *count = 1; return;
- #endif
- default: break;
+ if (identical) {
+ if (player->craft_items[4] == 0) {
+ // Center slot clear
+ switch (first_item) {
+ case I_cobblestone: *item = I_furnace; *count = 1; return;
+ #ifdef ALLOW_CHESTS
+ case I_oak_planks: *item = I_chest; *count = 1; return;
+ #endif
+ default: break;
+ }
+ } else if (player->craft_items[1] == 0) {
+ // Top-middle slot clear (chestplate recipes)
+ switch (first_item) {
+ case I_leather: *item = I_leather_chestplate; *count = 1; return;
+ case I_iron_ingot: *item = I_iron_chestplate; *count = 1; return;
+ case I_gold_ingot: *item = I_golden_chestplate; *count = 1; return;
+ case I_diamond: *item = I_diamond_chestplate; *count = 1; return;
+ case I_netherite_ingot: *item = I_netherite_chestplate; *count = 1; return;
+ default: break;
+ }
}
}
break;
blob - e341cd4362eb8db374d2b528086c6dee93389072
blob + 822f36e05d74a4b7691e866911a6eb1b1d3b7e04
--- src/procedures.c
+++ src/procedures.c
if (slot < 9) return slot + 36;
if (slot >= 9 && slot <= 35) return slot;
if (slot == 40) return 45;
- if (slot >= 36 && slot <= 39) return 3 - (slot - 36) + 5;
+ if (slot >= 36 && slot <= 39) return 44 - slot;
if (slot >= 41 && slot <= 44) return slot - 40;
} else if (window_id == 12) { // crafting table
if (slot >= 36 && slot <= 44) return slot - 36;
if (slot >= 9 && slot <= 35) return slot;
if (slot == 45) return 40;
- if (slot >= 5 && slot <= 8) return 4 - (slot - 5) + 36;
+ if (slot >= 5 && slot <= 8) return 44 - slot;
// map inventory crafting slots to player data crafting grid (semi-hack)
// this abuses the fact that the buffers are adjacent in player data
return 64;
}
+// Returns defense points for the given piece of armor
+// If the input item is not armor, returns 0
+uint8_t getItemDefensePoints (uint16_t item) {
+
+ switch (item) {
+ case I_leather_helmet: return 1;
+ case I_golden_helmet: return 2;
+ case I_iron_helmet: return 2;
+ case I_diamond_helmet: // Same as netherite
+ case I_netherite_helmet: return 3;
+ case I_leather_chestplate: return 3;
+ case I_golden_chestplate: return 5;
+ case I_iron_chestplate: return 6;
+ case I_diamond_chestplate: // Same as netherite
+ case I_netherite_chestplate: return 8;
+ case I_leather_leggings: return 2;
+ case I_golden_leggings: return 3;
+ case I_iron_leggings: return 5;
+ case I_diamond_leggings: // Same as netherite
+ case I_netherite_leggings: return 6;
+ case I_leather_boots: return 1;
+ case I_golden_boots: return 1;
+ case I_iron_boots: return 2;
+ case I_diamond_boots: // Same as netherite
+ case I_netherite_boots: return 3;
+ default: break;
+ }
+
+ return 0;
+}
+
+// Calculates total defense points for the player's equipped armor
+uint8_t getPlayerDefensePoints (PlayerData *player) {
+ return (
+ // Helmet
+ getItemDefensePoints(player->inventory_items[39]) +
+ // Chestplate
+ getItemDefensePoints(player->inventory_items[38]) +
+ // Leggings
+ getItemDefensePoints(player->inventory_items[37]) +
+ // Boots
+ getItemDefensePoints(player->inventory_items[36])
+ );
+}
+
+// Returns the designated server slot for the given piece of armor
+// If input item is not armor, returns 255
+uint8_t getArmorItemSlot (uint16_t item) {
+
+ switch (item) {
+ case I_leather_helmet:
+ case I_golden_helmet:
+ case I_iron_helmet:
+ case I_diamond_helmet:
+ case I_netherite_helmet:
+ return 39;
+ case I_leather_chestplate:
+ case I_golden_chestplate:
+ case I_iron_chestplate:
+ case I_diamond_chestplate:
+ case I_netherite_chestplate:
+ return 38;
+ case I_leather_leggings:
+ case I_golden_leggings:
+ case I_iron_leggings:
+ case I_diamond_leggings:
+ case I_netherite_leggings:
+ return 37;
+ case I_leather_boots:
+ case I_golden_boots:
+ case I_iron_boots:
+ case I_diamond_boots:
+ case I_netherite_boots:
+ return 36;
+ default: break;
+ }
+
+ return 255;
+}
+
// Handles the player eating their currently held item
// Returns whether the operation was succesful (item was consumed)
// If `just_check` is set to true, the item doesn't get consumed
// Reset eating timer and set eating flag
player->flagval_16 = 0;
player->flags |= 0x10;
+ } else if (getItemDefensePoints(*item) != 0) {
+ // For some reason, this action is sent twice when looking at a block
+ // Ignore the variant that has coordinates
+ if (face == 255) return;
+ // Swap to held piece of armor
+ uint8_t slot = getArmorItemSlot(*item);
+ uint16_t prev_item = player->inventory_items[slot];
+ player->inventory_items[slot] = *item;
+ player->inventory_count[slot] = 1;
+ player->inventory_items[player->hotbar] = prev_item;
+ player->inventory_count[player->hotbar] = 1;
+ // Update client inventory
+ sc_setContainerSlot(player->client_fd, -2, serverSlotToClientSlot(0, slot), 1, *item);
+ sc_setContainerSlot(player->client_fd, -2, serverSlotToClientSlot(0, player->hotbar), 1, prev_item);
+ return;
}
// Don't proceed with block placement if no coordinates were provided
// Don't continue if the player is already dead
if (player->health == 0) return;
+ // Calculate damage reduction from player's armor
+ uint8_t defense = getPlayerDefensePoints(player);
+ // This uses the old (pre-1.9) protection calculation. Factors are
+ // scaled up 256 times to avoid floating point math. Due to lost
+ // precision, the 4% reduction factor drops to ~3.9%, although the
+ // the resulting effective damage is then also rounded down.
+ uint8_t effective_damage = damage * (256 - defense * 10) / 256;
+
// Process health change on the server
- if (player->health <= damage) {
+ if (player->health <= effective_damage) {
player->health = 0;
entity_died = true;
recv_buffer[player_name_len + 14 + strlen(attacker->name)] = '\0';
}
- } else player->health -= damage;
+ } else player->health -= effective_damage;
// Update health on the client
sc_setHealth(entity_id, player->health, player->hunger, player->saturation);