mirror of
https://github.com/p2r3/bareiron.git
synced 2025-10-01 23:25:09 +02:00
implement mob combat
This commit is contained in:
@@ -93,6 +93,9 @@ typedef struct {
|
|||||||
short x;
|
short x;
|
||||||
uint8_t y;
|
uint8_t y;
|
||||||
short z;
|
short z;
|
||||||
|
// Lower 5 bits: health
|
||||||
|
// Upper 3 bits: reserved (?)
|
||||||
|
uint8_t data;
|
||||||
} MobData;
|
} MobData;
|
||||||
|
|
||||||
#pragma pack(pop)
|
#pragma pack(pop)
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
#ifndef H_PACKETS
|
#ifndef H_PACKETS
|
||||||
#define H_PACKETS
|
#define H_PACKETS
|
||||||
|
|
||||||
|
// Serverbound packets
|
||||||
int cs_handshake (int client_fd);
|
int cs_handshake (int client_fd);
|
||||||
int cs_loginStart (int client_fd, uint8_t *uuid, char *name);
|
int cs_loginStart (int client_fd, uint8_t *uuid, char *name);
|
||||||
int cs_clientInformation (int client_fd);
|
int cs_clientInformation (int client_fd);
|
||||||
@@ -17,6 +18,7 @@ int cs_closeContainer (int client_fd);
|
|||||||
int cs_clientStatus (int client_fd);
|
int cs_clientStatus (int client_fd);
|
||||||
int cs_chat(int client_fd);
|
int cs_chat(int client_fd);
|
||||||
|
|
||||||
|
// Clientbound packets
|
||||||
int sc_loginSuccess (int client_fd, uint8_t *uuid, char *name);
|
int sc_loginSuccess (int client_fd, uint8_t *uuid, char *name);
|
||||||
int sc_knownPacks (int client_fd);
|
int sc_knownPacks (int client_fd);
|
||||||
int sc_finishConfiguration (int client_fd);
|
int sc_finishConfiguration (int client_fd);
|
||||||
@@ -44,6 +46,9 @@ int sc_damageEvent (int client_fd, int id, int type);
|
|||||||
int sc_setHealth (int client_fd, uint8_t health, uint8_t food);
|
int sc_setHealth (int client_fd, uint8_t health, uint8_t food);
|
||||||
int sc_respawn (int client_fd);
|
int sc_respawn (int client_fd);
|
||||||
int sc_systemChat (int client_fd, char* message, uint16_t len);
|
int sc_systemChat (int client_fd, char* message, uint16_t len);
|
||||||
|
int cs_interact (int client_fd);
|
||||||
|
int sc_entityEvent (int client_fd, int entity_id, uint8_t status);
|
||||||
|
int sc_removeEntity (int client_fd, int entity_id);
|
||||||
int sc_registries (int client_fd);
|
int sc_registries (int client_fd);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@@ -31,7 +31,8 @@ uint16_t getMiningResult (uint16_t held_item, uint8_t block);
|
|||||||
void bumpToolDurability (PlayerData *player);
|
void bumpToolDurability (PlayerData *player);
|
||||||
void handlePlayerAction (PlayerData *player, int action, short x, short y, short z);
|
void handlePlayerAction (PlayerData *player, int action, short x, short y, short z);
|
||||||
|
|
||||||
void spawnMob (uint8_t type, short x, uint8_t y, short z);
|
void spawnMob (uint8_t type, short x, uint8_t y, short z, uint8_t health);
|
||||||
|
void hurtEntity (int entity_id, int attacker_id, uint8_t damage_type, uint8_t damage);
|
||||||
void handleServerTick (int64_t time_since_last_tick);
|
void handleServerTick (int64_t time_since_last_tick);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
27
src/main.c
27
src/main.c
@@ -158,14 +158,20 @@ void handlePacket (int client_fd, int length, int packet_id) {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x1B: {
|
case 0x1B:
|
||||||
if (state == STATE_PLAY) {
|
if (state == STATE_PLAY) {
|
||||||
// Serverbound keep-alive (ignored)
|
// Serverbound keep-alive (ignored)
|
||||||
recv_all(client_fd, recv_buffer, 8, false);
|
recv_all(client_fd, recv_buffer, 8, false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
|
case 0x19:
|
||||||
|
if (state == STATE_PLAY) {
|
||||||
|
if (cs_interact(client_fd)) break;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case 0x1D:
|
case 0x1D:
|
||||||
case 0x1E:
|
case 0x1E:
|
||||||
@@ -190,10 +196,7 @@ void handlePacket (int client_fd, int length, int packet_id) {
|
|||||||
if (on_ground) {
|
if (on_ground) {
|
||||||
int8_t damage = player->grounded_y - player->y - 3;
|
int8_t damage = player->grounded_y - player->y - 3;
|
||||||
if (damage > 0 && getBlockAt(player->x, player->y, player->z) != B_water) {
|
if (damage > 0 && getBlockAt(player->x, player->y, player->z) != B_water) {
|
||||||
if (damage >= player->health) player->health = 0;
|
hurtEntity(client_fd, -1, D_fall, damage);
|
||||||
else player->health -= damage;
|
|
||||||
sc_damageEvent(client_fd, client_fd, D_fall);
|
|
||||||
sc_setHealth(client_fd, player->health, player->hunger);
|
|
||||||
}
|
}
|
||||||
player->grounded_y = player->y;
|
player->grounded_y = player->y;
|
||||||
}
|
}
|
||||||
@@ -295,14 +298,14 @@ void handlePacket (int client_fd, int length, int packet_id) {
|
|||||||
getBlockAt(mob_x, mob_y + 1, mob_z) == B_air
|
getBlockAt(mob_x, mob_y + 1, mob_z) == B_air
|
||||||
) {
|
) {
|
||||||
// Spawn passive mobs during the day, hostiles during the night
|
// Spawn passive mobs during the day, hostiles during the night
|
||||||
if (world_time < 12000) {
|
if (world_time < 13000) {
|
||||||
uint32_t mob_choice = (r >> 12) & 3;
|
uint32_t mob_choice = (r >> 12) & 3;
|
||||||
if (mob_choice == 0) spawnMob(25, mob_x, mob_y, mob_z); // Chicken
|
if (mob_choice == 0) spawnMob(25, mob_x, mob_y, mob_z, 20); // Chicken
|
||||||
else if (mob_choice == 1) spawnMob(28, mob_x, mob_y, mob_z); // Cow
|
else if (mob_choice == 1) spawnMob(28, mob_x, mob_y, mob_z, 20); // Cow
|
||||||
else if (mob_choice == 2) spawnMob(95, mob_x, mob_y, mob_z); // Pig
|
else if (mob_choice == 2) spawnMob(95, mob_x, mob_y, mob_z, 20); // Pig
|
||||||
else if (mob_choice == 3) spawnMob(106, mob_x, mob_y, mob_z); // Sheep
|
else if (mob_choice == 3) spawnMob(106, mob_x, mob_y, mob_z, 20); // Sheep
|
||||||
} else {
|
} else {
|
||||||
spawnMob(145, mob_x, mob_y, mob_z); // Zombie
|
spawnMob(145, mob_x, mob_y, mob_z, 20); // Zombie
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -973,6 +973,52 @@ int cs_chat (int client_fd) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// C->S Interact
|
||||||
|
int cs_interact (int client_fd) {
|
||||||
|
|
||||||
|
int entity_id = readVarInt(client_fd);
|
||||||
|
uint8_t type = readByte(client_fd);
|
||||||
|
|
||||||
|
if (type == 2) {
|
||||||
|
// Ignore target coordinates
|
||||||
|
recv_all(client_fd, recv_buffer, 12, false);
|
||||||
|
}
|
||||||
|
if (type != 1) {
|
||||||
|
// Ignore hand
|
||||||
|
recv_all(client_fd, recv_buffer, 1, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ignore sneaking flag
|
||||||
|
recv_all(client_fd, recv_buffer, 1, false);
|
||||||
|
|
||||||
|
hurtEntity(entity_id, client_fd, D_generic, 1);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// S->C Entity Event
|
||||||
|
int sc_entityEvent (int client_fd, int entity_id, uint8_t status) {
|
||||||
|
|
||||||
|
writeVarInt(client_fd, 6);
|
||||||
|
writeByte(client_fd, 0x1E);
|
||||||
|
|
||||||
|
writeUint32(client_fd, entity_id);
|
||||||
|
writeByte(client_fd, status);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// S->C Remove Entities, but for only one entity per packet
|
||||||
|
int sc_removeEntity (int client_fd, int entity_id) {
|
||||||
|
|
||||||
|
writeVarInt(client_fd, 2 + sizeVarInt(entity_id));
|
||||||
|
writeByte(client_fd, 0x46);
|
||||||
|
|
||||||
|
writeByte(client_fd, 1);
|
||||||
|
writeVarInt(client_fd, entity_id);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// S->C Registry Data (multiple packets) and Update Tags (configuration, multiple packets)
|
// S->C Registry Data (multiple packets) and Update Tags (configuration, multiple packets)
|
||||||
int sc_registries (int client_fd) {
|
int sc_registries (int client_fd) {
|
||||||
|
|
||||||
|
@@ -485,7 +485,7 @@ void handlePlayerAction (PlayerData *player, int action, short x, short y, short
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void spawnMob (uint8_t type, short x, uint8_t y, short z) {
|
void spawnMob (uint8_t type, short x, uint8_t y, short z, uint8_t health) {
|
||||||
|
|
||||||
for (int i = 0; i < MAX_MOBS; i ++) {
|
for (int i = 0; i < MAX_MOBS; i ++) {
|
||||||
// Look for type 0 (unallocated)
|
// Look for type 0 (unallocated)
|
||||||
@@ -496,6 +496,7 @@ void spawnMob (uint8_t type, short x, uint8_t y, short z) {
|
|||||||
mob_data[i].x = x;
|
mob_data[i].x = x;
|
||||||
mob_data[i].y = y;
|
mob_data[i].y = y;
|
||||||
mob_data[i].z = z;
|
mob_data[i].z = z;
|
||||||
|
mob_data[i].data = health & 31;
|
||||||
|
|
||||||
// Broadcast entity creation to all players
|
// Broadcast entity creation to all players
|
||||||
for (int j = 0; j < MAX_PLAYERS; j ++) {
|
for (int j = 0; j < MAX_PLAYERS; j ++) {
|
||||||
@@ -515,6 +516,56 @@ void spawnMob (uint8_t type, short x, uint8_t y, short z) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void hurtEntity (int entity_id, int attacker_id, uint8_t damage_type, uint8_t damage) {
|
||||||
|
|
||||||
|
// Retrieve player data if applicable
|
||||||
|
PlayerData *player;
|
||||||
|
if (attacker_id < 65536 && attacker_id != -1) {
|
||||||
|
if (getPlayerData(attacker_id, &player)) return;
|
||||||
|
} else if (entity_id < 65536) {
|
||||||
|
if (getPlayerData(entity_id, &player)) return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (attacker_id < 65536 && attacker_id != -1) { // Attacker is a player
|
||||||
|
// Scale damage based on held item
|
||||||
|
uint16_t held_item = player->inventory_items[player->hotbar];
|
||||||
|
if (held_item == I_wooden_sword) damage *= 4;
|
||||||
|
else if (held_item == I_golden_sword) damage *= 4;
|
||||||
|
else if (held_item == I_stone_sword) damage *= 5;
|
||||||
|
else if (held_item == I_iron_sword) damage *= 6;
|
||||||
|
else if (held_item == I_diamond_sword) damage *= 7;
|
||||||
|
else if (held_item == I_netherite_sword) damage *= 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Whether this attack caused the target entity to die
|
||||||
|
uint8_t entity_died = false;
|
||||||
|
|
||||||
|
if (entity_id < 65536) { // The attacked entity is a player
|
||||||
|
if (player->health <= damage) {
|
||||||
|
player->health = 0;
|
||||||
|
entity_died = true;
|
||||||
|
} else player->health -= damage;
|
||||||
|
// Update health on the client
|
||||||
|
sc_setHealth(player->client_fd, player->health, player->hunger);
|
||||||
|
} else { // The attacked entity is a mob
|
||||||
|
MobData *mob = &mob_data[entity_id - 65536];
|
||||||
|
uint8_t mob_health = mob->data & 31;
|
||||||
|
if (mob_health <= damage) {
|
||||||
|
mob->data -= mob_health;
|
||||||
|
entity_died = true;
|
||||||
|
} else mob->data -= damage;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Broadcast damage event to all players
|
||||||
|
for (int i = 0; i < MAX_PLAYERS; i ++) {
|
||||||
|
int client_fd = player_data[i].client_fd;
|
||||||
|
if (client_fd == -1) continue;
|
||||||
|
sc_damageEvent(client_fd, entity_id, damage_type);
|
||||||
|
if (entity_died) sc_entityEvent(client_fd, entity_id, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// Simulates events scheduled for regular intervals
|
// Simulates events scheduled for regular intervals
|
||||||
// Takes the time since the last tick in microseconds as the only arguemnt
|
// Takes the time since the last tick in microseconds as the only arguemnt
|
||||||
void handleServerTick (int64_t time_since_last_tick) {
|
void handleServerTick (int64_t time_since_last_tick) {
|
||||||
@@ -540,6 +591,19 @@ void handleServerTick (int64_t time_since_last_tick) {
|
|||||||
for (int i = 0; i < MAX_MOBS; i ++) {
|
for (int i = 0; i < MAX_MOBS; i ++) {
|
||||||
if (mob_data[i].type == 0) continue;
|
if (mob_data[i].type == 0) continue;
|
||||||
|
|
||||||
|
// Mob has died, deallocate it
|
||||||
|
if ((mob_data[i].data & 31) == 0) {
|
||||||
|
mob_data[i].type = 0;
|
||||||
|
for (int j = 0; j < MAX_PLAYERS; j ++) {
|
||||||
|
if (player_data[j].client_fd == -1) continue;
|
||||||
|
// Spawn death smoke particles
|
||||||
|
sc_entityEvent(player_data[j].client_fd, 65536 + i, 60);
|
||||||
|
// Remove the entity from the client
|
||||||
|
sc_removeEntity(player_data[j].client_fd, 65536 + i);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
uint8_t passive = (
|
uint8_t passive = (
|
||||||
mob_data[i].type == 25 || // Chicken
|
mob_data[i].type == 25 || // Chicken
|
||||||
mob_data[i].type == 28 || // Cow
|
mob_data[i].type == 28 || // Cow
|
||||||
@@ -554,7 +618,7 @@ void handleServerTick (int64_t time_since_last_tick) {
|
|||||||
|
|
||||||
// Find the player closest to this mob
|
// Find the player closest to this mob
|
||||||
PlayerData* closest_player;
|
PlayerData* closest_player;
|
||||||
uint32_t closest_dist = 65535;
|
uint32_t closest_dist = 65536;
|
||||||
for (int j = 0; j < MAX_PLAYERS; j ++) {
|
for (int j = 0; j < MAX_PLAYERS; j ++) {
|
||||||
if (player_data[j].client_fd == -1) continue;
|
if (player_data[j].client_fd == -1) continue;
|
||||||
uint16_t curr_dist = (
|
uint16_t curr_dist = (
|
||||||
@@ -594,10 +658,7 @@ void handleServerTick (int64_t time_since_last_tick) {
|
|||||||
|
|
||||||
// If we're already next to the player, hurt them and skip movement
|
// If we're already next to the player, hurt them and skip movement
|
||||||
if (closest_dist < 3) {
|
if (closest_dist < 3) {
|
||||||
if (closest_player->health < 6) closest_player->health = 0;
|
hurtEntity(closest_player->client_fd, 65536 + i, D_generic, 6);
|
||||||
else closest_player->health -= 6;
|
|
||||||
sc_damageEvent(closest_player->client_fd, closest_player->client_fd, D_generic);
|
|
||||||
sc_setHealth(closest_player->client_fd, closest_player->health, closest_player->hunger);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user