Броня в семи местах и три зоны урона

- В реестр заведены 46 предметов одежды ремейка: слот, слой, какие части тела
  закрывает и сколько снимает из каждого вида вреда. Данные выгружены в items.csv
  (extract-items.py), в коде у каждого предмета armor(...).
- Семь мест: голова, лицо, грудь (2 слоя), ноги (2 слоя), руки, стопы, полный
  костюм. Пояс не делали - в ремейке его нет, есть только быстрая панель.
- Inventory сервера: семь мест по два слоя, вещи надеваются и снимаются.
- Damage: зона урона по точке попадания (голова верхние 0.4, ноги нижние 0.4),
  множители голова 2, тело 1, ноги 0.75; одежда снимает вред только там, где
  закрывает.
- Пакет Attack (цель, точка попадания, рука) и клиентский патч, который его
  шлёт. У игрока появилась жизнь.
- В items.csv добавлены колонки armor_slot, armor_layer, armor_zones.

Тесты: протокол 24 + 10, сервер 85. Форк собирается чисто, 3279 классов.
This commit is contained in:
sasheg dev
2026-10-21 22:43:52 +03:00
parent 9980c1561d
commit 7505ddd84e
18 changed files with 1188 additions and 262 deletions
@@ -158,6 +158,9 @@ public final class Packets {
/** The client asks the server to read its box file again. */
public static final int HITBOXES_RELOAD = 0x2C;
/** The client hits something. */
public static final int ATTACK = 0x2D;
/**
* One slot of the inventory and what is in it now.
*
@@ -931,19 +934,38 @@ public final class Packets {
/** The slot the player is holding. */
public final int held;
/**
* The clothing the player wears: seven places, two layers each.
*
* The order is the head, the face, the chest, the legs, the hands,
* the feet and the whole body, and inside each place the clothes
* under come before the armour over.
*/
public final Slot[] armor;
public Inventory(Slot[] slots, int held) {
this(slots, held, new Slot[0]);
}
public Inventory(Slot[] slots, int held, Slot[] armor) {
this.slots = slots;
this.held = held;
this.armor = armor == null ? new Slot[0] : armor;
}
public Buf write() {
Buf buf = new Buf(16 + this.slots.length * 12);
Buf buf = new Buf(16 + (this.slots.length + this.armor.length) * 12);
buf.writeVarInt(this.held);
buf.writeVarInt(this.slots.length);
for (int i = 0; i < this.slots.length; i++) {
Slot slot = this.slots[i] == null ? Slot.of(null) : this.slots[i];
slot.write(buf);
}
buf.writeVarInt(this.armor.length);
for (int i = 0; i < this.armor.length; i++) {
Slot slot = this.armor[i] == null ? Slot.of(null) : this.armor[i];
slot.write(buf);
}
return buf;
}
@@ -957,7 +979,15 @@ public final class Packets {
for (int i = 0; i < count; i++) {
slots[i] = Slot.read(in);
}
return new Inventory(slots, held);
int worn = in.readVarInt();
if (worn < 0 || worn > 64) {
throw new ProtocolException("that much clothing is not real");
}
Slot[] armor = new Slot[worn];
for (int i = 0; i < worn; i++) {
armor[i] = Slot.read(in);
}
return new Inventory(slots, held, armor);
}
}
@@ -1531,6 +1561,49 @@ public final class Packets {
}
}
/**
* The client hits something.
*
* The place of the hit matters: a hit on the head hurts twice as much as
* a hit on the body, and the clothing only takes a part of the harm away
* where it covers the body.
*/
public static final class Attack implements Packet {
/** The hand that holds the weapon. */
public static final int HAND_MAIN = 0;
public static final int HAND_OFF = 1;
public final int target;
public final float x;
public final float y;
public final float z;
public final int hand;
public Attack(int target, float x, float y, float z, int hand) {
this.target = target;
this.x = x;
this.y = y;
this.z = z;
this.hand = hand;
}
public Buf write() {
Buf buf = new Buf(24);
buf.writeVarInt(this.target);
buf.writeFloat(this.x);
buf.writeFloat(this.y);
buf.writeFloat(this.z);
buf.writeVarInt(this.hand);
return buf;
}
public static Attack read(Buf in) {
return new Attack(in.readVarInt(), in.readFloat(), in.readFloat(),
in.readFloat(), in.readVarInt());
}
}
/**
* Every packet id this build knows, and what it decodes into.
*
@@ -1574,6 +1647,7 @@ public final class Packets {
map.put(Integer.valueOf(HITBOXES), "Hitboxes");
map.put(Integer.valueOf(HITBOX_INSTANCES), "HitboxInstances");
map.put(Integer.valueOf(HITBOXES_RELOAD), "HitboxesReload");
map.put(Integer.valueOf(ATTACK), "Attack");
map.put(Integer.valueOf(DISCONNECT), "Disconnect");
return map;
}