From 69a12dd2c8e58f8957dc42d7de1964bbad1b0868 Mon Sep 17 00:00:00 2001 From: IQuant Date: Wed, 8 May 2024 20:33:41 +0300 Subject: [PATCH] Kinda sync enemies --- README.md | 5 +++ files/src/ctx.lua | 2 + files/src/enemy_sync.lua | 87 ++++++++++++++++++++++++++++++++++++++ files/src/net.lua | 4 ++ files/src/net_handling.lua | 7 +++ files/src/util.lua | 27 ++++++++++++ init.lua | 16 +++++-- todo.txt | 9 +--- 8 files changed, 147 insertions(+), 10 deletions(-) create mode 100644 README.md create mode 100644 files/src/enemy_sync.lua diff --git a/README.md b/README.md new file mode 100644 index 00000000..4bb24c1b --- /dev/null +++ b/README.md @@ -0,0 +1,5 @@ +# Noita Entangled Worlds + + + +Special thanks to @EvaisaDev for allowing to use code from Noita Arena mod. \ No newline at end of file diff --git a/files/src/ctx.lua b/files/src/ctx.lua index 7594b01e..e72f2f6d 100644 --- a/files/src/ctx.lua +++ b/files/src/ctx.lua @@ -3,8 +3,10 @@ local ctx = { } ctx.init = function() + ctx.host_id = 0 ctx.my_id = nil ctx.players = {} + ctx.enemy_by_remote_id = {} end return ctx \ No newline at end of file diff --git a/files/src/enemy_sync.lua b/files/src/enemy_sync.lua new file mode 100644 index 00000000..2ca1c323 --- /dev/null +++ b/files/src/enemy_sync.lua @@ -0,0 +1,87 @@ +local util = dofile_once("mods/quant.ew/files/src/util.lua") +local ctx = dofile_once("mods/quant.ew/files/src/ctx.lua") + +local enemy_sync = {} + +local function world_exists_for(entity) + local x, y = EntityGetFirstHitboxCenter(entity) + local w, h = 5, 5 -- TODO + w = w * 0.5 + h = h * 0.5 + return DoesWorldExistAt(x - w, y - h, x + w, y + h) +end + +function enemy_sync.host_upload_entities() + local enemy_list = EntityGetWithTag("enemy") -- TODO maybe only sync those close to players? + local enemy_data_list = {} + for i, enemy_id in ipairs(enemy_list) do + if not world_exists_for(enemy_id) then + goto continue + end + local filename = EntityGetFilename(enemy_id) + --print("ent "..enemy_id.." "..filename) + local x, y = EntityGetTransform(enemy_id) + local hp, max_hp = util.get_ent_health(enemy_id) + table.insert(enemy_data_list, {enemy_id, filename, x, y, hp, max_hp}) + ::continue:: + end + --print(#enemy_data_list) + return enemy_data_list +end + +function enemy_sync.client_cleanup() + local enemy_list = EntityGetWithTag("enemy") + for i, enemy_id in ipairs(enemy_list) do + if not EntityHasTag(enemy_id, "ew_replicated") then + print("Despawning unreplicated "..enemy_id) + EntityKill(enemy_id) + end + end + local frame = GameGetFrameNum() + for remote_id, enemy_data in pairs(ctx.enemy_by_remote_id) do + if frame - enemy_data.frame > 60*10 then + print("Despawning stale "..remote_id) + EntityKill(enemy_data.id) + ctx.enemy_by_remote_id[remote_id] = nil + end + end +end + +function enemy_sync.handle_enemy_data(enemy_data) + -- GamePrint("Got enemy data") + for _, enemy_info_raw in ipairs(enemy_data) do + local remote_enemy_id = enemy_info_raw[1] + local filename = enemy_info_raw[2] + local x = enemy_info_raw[3] + local y = enemy_info_raw[4] + local hp = enemy_info_raw[5] + local max_hp = enemy_info_raw[6] + + local frame = GameGetFrameNum() + + if ctx.enemy_by_remote_id[remote_enemy_id] ~= nil and not EntityGetIsAlive(ctx.enemy_by_remote_id[remote_enemy_id].id) then + ctx.enemy_by_remote_id[remote_enemy_id] = nil + end + + if ctx.enemy_by_remote_id[remote_enemy_id] == nil then + local enemy_id = EntityLoad(filename, x, y) + EntityAddTag(enemy_id, "ew_replicated") + ctx.enemy_by_remote_id[remote_enemy_id] = {id = enemy_id, frame = frame} + end + + local enemy_data = ctx.enemy_by_remote_id[remote_enemy_id] + enemy_data.frame = frame + local enemy_id = enemy_data.id + local px, py = EntityGetTransform(enemy_id) + local tp_limit = 30 + local alpha = 0.2 + -- if math.pow(px-x, 2) + math.pow(py-y, 2) > math.pow(tp_limit, 2) then + EntitySetTransform(enemy_id, x, y) + -- else + -- EntitySetTransform(util.lerp(px, x, alpha), util.lerp(py, y, alpha)) + -- end + util.set_ent_health(enemy_id, {hp, max_hp}) + end +end + +return enemy_sync \ No newline at end of file diff --git a/files/src/net.lua b/files/src/net.lua index c9a3c63b..c2bb0b5c 100644 --- a/files/src/net.lua +++ b/files/src/net.lua @@ -99,4 +99,8 @@ function net.send_player_perks(perk_data) net.send("perks", perk_data, true) end +function net.send_enemy_data(enemy_data) + net.send("enemy", enemy_data) +end + return net \ No newline at end of file diff --git a/files/src/net_handling.lua b/files/src/net_handling.lua index 9644d4c7..66ae85ca 100644 --- a/files/src/net_handling.lua +++ b/files/src/net_handling.lua @@ -1,5 +1,6 @@ local player_fns = dofile_once("mods/quant.ew/files/src/player_fns.lua") local ctx = dofile_once("mods/quant.ew/files/src/ctx.lua") +local enemy_sync = dofile_once("mods/quant.ew/files/src/enemy_sync.lua") local perk_fns = dofile_once("mods/quant.ew/files/src/perk_fns.lua") local net_handling = { @@ -52,4 +53,10 @@ function net_handling.mod.perks(peer_id, perk_data) perk_fns.update_perks(perk_data, player_data) end +function net_handling.mod.enemy(peer_id, enemy_data) + if peer_id == ctx.host_id then + enemy_sync.handle_enemy_data(enemy_data) + end +end + return net_handling \ No newline at end of file diff --git a/files/src/util.lua b/files/src/util.lua index e8c722f8..0a463e86 100644 --- a/files/src/util.lua +++ b/files/src/util.lua @@ -15,4 +15,31 @@ function util.set_ent_variable(entity, key, value) ComponentSetValue2(storage, "value_string", bitser.dumps(value)) end +function util.get_ent_health(entity) + local damage_model = EntityGetFirstComponentIncludingDisabled(entity, "DamageModelComponent") + if damage_model == 0 then + return 0, 0 + end + local hp = ComponentGetValue2(damage_model, "hp") + local max_hp = ComponentGetValue2(damage_model, "max_hp") + return hp, max_hp +end + +function util.set_ent_health(entity, hp_data) + local damage_model = EntityGetFirstComponentIncludingDisabled(entity, "DamageModelComponent") + if damage_model == 0 then + return + end + if hp_data[1] ~= nil then + ComponentSetValue2(damage_model, "hp", hp_data[1]) + end + if hp_data[2] ~= nil then + ComponentSetValue2(damage_model, "max_hp", hp_data[2]) + end +end + +function util.lerp(a, b, alpha) + return a * alpha + b * (1 - alpha) +end + return util \ No newline at end of file diff --git a/init.lua b/init.lua index b26e258f..86590743 100755 --- a/init.lua +++ b/init.lua @@ -13,6 +13,7 @@ local net = dofile_once("mods/quant.ew/files/src/net.lua") local ctx = dofile_once("mods/quant.ew/files/src/ctx.lua") local pretty = dofile_once("mods/quant.ew/files/lib/pretty_print.lua") local perk_fns = dofile_once("mods/quant.ew/files/src/perk_fns.lua") +local enemy_sync = dofile_once("mods/quant.ew/files/src/enemy_sync.lua") function OnProjectileFired(shooter_id, projectile_id, rng, position_x, position_y, target_x, target_y, send_message, unknown1, multicast_index, unknown3) @@ -77,14 +78,15 @@ function OnPlayerSpawned( player_entity ) -- This runs when player entity has be GamePrint("My peer_id: "..ctx.my_id) ctx.players[ctx.my_id] = my_player ctx.ready = true + ctx.is_host = ctx.my_id == ctx.host_id np.SetPauseState(4) np.SetPauseState(0) dofile_once("data/scripts/perks/perk.lua") local x, y = EntityGetFirstHitboxCenter(player_entity) - perk_spawn(x, y, "LASER_AIM") - perk_spawn(x-50, y, "GLASS_CANNON") + perk_spawn(x, y, "LASER_AIM", true) + perk_spawn(x-50, y, "GLASS_CANNON", true) end function OnWorldPreUpdate() -- This is called every time the game is about to start updating the world @@ -92,13 +94,21 @@ function OnWorldPreUpdate() -- This is called every time the game is about to st -- GamePrint( "Pre-update hook " .. tostring(GameGetFrameNum()) ) net.update() - if GameGetFrameNum() % 2 == 0 then + if GameGetFrameNum() % 1 == 0 then local input_data = player_fns.serialize_inputs(my_player) local pos_data = player_fns.serialize_position(my_player) local current_slot = player_fns.get_current_slot(my_player) net.send_player_update(input_data, pos_data, current_slot) end + if GameGetFrameNum() % 2 == 1 then + if ctx.is_host then + net.send_enemy_data(enemy_sync.host_upload_entities()) + else + enemy_sync.client_cleanup() + end + end + if GameGetFrameNum() % 120 == 0 then local inventory_state = player_fns.serialize_items(my_player) net.send_player_inventory(inventory_state) diff --git a/todo.txt b/todo.txt index 556b1de5..908899f3 100644 --- a/todo.txt +++ b/todo.txt @@ -1,14 +1,9 @@ + Синхронизация перков -#- ShotEffectComponent + k Синхронизация противников - Общее хп - Синхронизация хп - Проверка окончания игры - Удаление лишних клиентов при загрузке игры - -#- Immunities -#- Sectors -#- Telekenesis -#- Unli - Fix weird no gui mode @@ -22,10 +17,10 @@ + ...и текущего предмета - reliability + Сжатие пакетов - - Синхронизация противников - Перекидывание предметов - Синхронизация мира ? Фикс наведения на клиентов - fungal shift-ы + - orbs sync - test tangled better - poly on clients \ No newline at end of file