From e8e7fd9dd9a5a02ac2342560a46c92391fe49626 Mon Sep 17 00:00:00 2001 From: xeon Date: Sun, 8 Feb 2026 18:42:48 +0300 Subject: [PATCH] feat(scene): implement enemy spawn --- .../Assets/configs/WorldEntityRegistry.zig | 23 ++++++ gamesv/src/logic/event/kinds.zig | 2 + gamesv/src/logic/messaging/scene.zig | 2 + gamesv/src/logic/systems/scene.zig | 77 +++++++++++++++++++ 4 files changed, 104 insertions(+) diff --git a/gamesv/src/Assets/configs/WorldEntityRegistry.zig b/gamesv/src/Assets/configs/WorldEntityRegistry.zig index d23c539..2c955ae 100644 --- a/gamesv/src/Assets/configs/WorldEntityRegistry.zig +++ b/gamesv/src/Assets/configs/WorldEntityRegistry.zig @@ -9,4 +9,27 @@ pub const WorldEntityBriefInfo = struct { detailId: ?[]const u8, position: logic.Level.Object.Vector, rotation: logic.Level.Object.Vector, + + pub fn objectType(info: WorldEntityBriefInfo) ObjectType { + return std.enums.fromInt(ObjectType, info.entityType) orelse .invalid; + } +}; + +pub const ObjectType = enum(u32) { + all = 4294967295, + invalid = 1, + character = 8, + enemy = 16, + interactive = 32, + projectile = 64, + factory_region = 128, + npc = 256, + ability_entity = 512, + cinematic_entity = 1024, + remote_factory_entity = 2048, + creature = 4096, + god_entity = 8192, + enemy_part = 16384, + social_building = 32768, + enemy_all = 16400, }; diff --git a/gamesv/src/logic/event/kinds.zig b/gamesv/src/logic/event/kinds.zig index ae3944d..e2af41e 100644 --- a/gamesv/src/logic/event/kinds.zig +++ b/gamesv/src/logic/event/kinds.zig @@ -18,3 +18,5 @@ pub const SyncSelfScene = struct { team_modified, }, }; + +pub const RefreshVisibleObjects = struct {}; diff --git a/gamesv/src/logic/messaging/scene.zig b/gamesv/src/logic/messaging/scene.zig index e2e3859..b9cdb78 100644 --- a/gamesv/src/logic/messaging/scene.zig +++ b/gamesv/src/logic/messaging/scene.zig @@ -9,8 +9,10 @@ const Player = logic.Player; pub fn onSceneLoadFinish( _: messaging.Request(pb.CS_SCENE_LOAD_FINISH), sync_self_scene_tx: logic.event.Sender(.sync_self_scene), + refresh_visible_objects_tx: logic.event.Sender(.refresh_visible_objects), ) !void { try sync_self_scene_tx.send(.{ .reason = .entrance }); + try refresh_visible_objects_tx.send(.{}); } pub fn onMoveObjectMove( diff --git a/gamesv/src/logic/systems/scene.zig b/gamesv/src/logic/systems/scene.zig index c83bdda..07f816c 100644 --- a/gamesv/src/logic/systems/scene.zig +++ b/gamesv/src/logic/systems/scene.zig @@ -202,6 +202,83 @@ pub fn syncSelfScene( try session.send(self_scene_info); } +pub fn refreshVisibleObjects( + rx: logic.event.Receiver(.refresh_visible_objects), + session: *Session, + arena: logic.Resource.Allocator(.arena), + scene: Player.Component(.scene), + assets: *const Assets, +) !void { + const objects_per_message: usize = 128; // TODO(Session): message fragmentation. + + _ = rx; + + const level_id = scene.data.current.level_id; + var container: pb.SCENE_OBJECT_DETAIL_CONTAINER = .init; + + const brief_map = &assets.world_entity_registry.worldEntityBriefInfos; + for (brief_map.map.keys(), brief_map.map.values()) |id, info| { + if (@divFloor(id, 100_000_000) != level_id) + continue; + + if (info.objectType() != .enemy) continue; + const template_id = info.detailId orelse continue; + + const enemy_attrs = assets.table(.enemy_attribute_template).getPtr(template_id) orelse continue; + const common_info: pb.SCENE_OBJECT_COMMON_INFO = .{ + .id = id, + .type = 3, + .templateid = template_id, + .position = .{ .X = info.position.x, .Y = info.position.y, .Z = info.position.z }, + .rotation = .{ .X = info.rotation.x, .Y = info.rotation.y, .Z = info.rotation.z }, + .scene_num_id = level_id, + .hp = 100, + }; + + var monster: pb.SCENE_MONSTER = .{ + .common_info = common_info, + .level = 1, + .battle_info = .{ + .msg_generation = @truncate(id), + .battle_inst_id = @truncate(id), + .part_inst_info = .init, + }, + }; + + for (enemy_attrs.levelDependentAttributes[0].attrs) |attr| { + try monster.attrs.append(arena.interface, .{ + .attr_type = @intFromEnum(attr.attrType), + .basic_value = attr.attrValue, + .value = attr.attrValue, + }); + } + + for (enemy_attrs.levelIndependentAttributes.attrs) |attr| { + try monster.attrs.append(arena.interface, .{ + .attr_type = @intFromEnum(attr.attrType), + .basic_value = attr.attrValue, + .value = attr.attrValue, + }); + } + + try container.monster_list.append(arena.interface, monster); + + if (container.monster_list.items.len == objects_per_message) { + try session.send(pb.SC_OBJECT_ENTER_VIEW{ + .detail = container, + }); + + container = .init; + } + } + + if (container.monster_list.items.len != 0) { + try session.send(pb.SC_OBJECT_ENTER_VIEW{ + .detail = container, + }); + } +} + fn packCharacterSkills( arena: Allocator, assets: *const Assets,