2024-05-08 20:33:41 +03:00
|
|
|
local util = dofile_once("mods/quant.ew/files/src/util.lua")
|
|
|
|
local ctx = dofile_once("mods/quant.ew/files/src/ctx.lua")
|
2024-05-30 12:41:56 +03:00
|
|
|
local net = dofile_once("mods/quant.ew/files/src/net.lua")
|
2024-06-02 13:49:11 +03:00
|
|
|
local player_fns = dofile_once("mods/quant.ew/files/src/player_fns.lua")
|
|
|
|
local np = require("noitapatcher")
|
2024-05-30 12:41:56 +03:00
|
|
|
|
|
|
|
local rpc = net.new_rpc_namespace()
|
2024-05-08 20:33:41 +03:00
|
|
|
|
|
|
|
local enemy_sync = {}
|
|
|
|
|
2024-06-02 13:49:11 +03:00
|
|
|
local dead_entities = {}
|
|
|
|
|
|
|
|
np.CrossCallAdd("ew_es_death_notify", function(enemy_id, responsible_id)
|
|
|
|
local player_data = player_fns.get_player_data_by_local_entity_id(responsible_id)
|
|
|
|
local responsible = nil
|
|
|
|
if player_data ~= nil then
|
|
|
|
responsible = player_data.peer_id
|
|
|
|
else
|
|
|
|
responsible = responsible_id
|
|
|
|
end
|
|
|
|
table.insert(dead_entities, {enemy_id, responsible})
|
|
|
|
end)
|
|
|
|
|
2024-05-08 20:33:41 +03:00
|
|
|
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
|
|
|
|
|
2024-05-09 23:37:50 +03:00
|
|
|
local function table_extend(to, from)
|
|
|
|
for _, e in ipairs(from) do
|
|
|
|
to[#to+1] = e
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
local function table_extend_filtered(to, from, filter)
|
|
|
|
for _, e in ipairs(from) do
|
|
|
|
if filter(e) then
|
|
|
|
to[#to+1] = e
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2024-05-10 10:30:14 +03:00
|
|
|
local function get_sync_entities()
|
2024-05-09 23:37:50 +03:00
|
|
|
local entities = EntityGetWithTag("enemy") -- TODO maybe only sync those close to players?
|
|
|
|
table_extend_filtered(entities, EntityGetWithTag("projectile"), function (ent)
|
2024-05-15 14:23:40 +03:00
|
|
|
return not (EntityHasTag(ent, "ew_shot_by_player") or EntityHasTag(ent, "projectile_player"))
|
2024-05-09 23:37:50 +03:00
|
|
|
end)
|
2024-05-10 10:30:14 +03:00
|
|
|
return entities
|
|
|
|
end
|
|
|
|
|
|
|
|
function enemy_sync.host_upload_entities()
|
|
|
|
local entities = get_sync_entities()
|
2024-05-08 20:33:41 +03:00
|
|
|
local enemy_data_list = {}
|
2024-05-09 23:37:50 +03:00
|
|
|
for i, enemy_id in ipairs(entities) do
|
2024-05-08 20:33:41 +03:00
|
|
|
if not world_exists_for(enemy_id) then
|
|
|
|
goto continue
|
|
|
|
end
|
|
|
|
local filename = EntityGetFilename(enemy_id)
|
|
|
|
local x, y = EntityGetTransform(enemy_id)
|
2024-05-09 23:37:50 +03:00
|
|
|
local character_data = EntityGetFirstComponentIncludingDisabled(enemy_id, "VelocityComponent")
|
2024-05-08 22:13:17 +03:00
|
|
|
local vx, vy = 0, 0
|
2024-05-09 23:37:50 +03:00
|
|
|
if character_data ~= nil then
|
2024-05-08 22:13:17 +03:00
|
|
|
vx, vy = ComponentGetValue2(character_data, "mVelocity")
|
|
|
|
end
|
2024-05-09 23:37:50 +03:00
|
|
|
local ai_component = EntityGetFirstComponentIncludingDisabled(enemy_id, "AnimalAIComponent")
|
|
|
|
if ai_component ~= nil then
|
|
|
|
ComponentSetValue2(ai_component, "max_distance_to_cam_to_start_hunting", math.pow(2, 29))
|
|
|
|
end
|
2024-05-21 10:48:40 +03:00
|
|
|
local hp, max_hp, has_hp = util.get_ent_health(enemy_id)
|
|
|
|
|
|
|
|
if has_hp then
|
|
|
|
util.ensure_component_present(enemy_id, "LuaComponent", "ew_death_notify", {
|
|
|
|
script_death = "mods/quant.ew/files/cbs/death_notify.lua"
|
|
|
|
})
|
|
|
|
end
|
2024-05-12 18:05:25 +03:00
|
|
|
|
2024-05-08 22:13:17 +03:00
|
|
|
table.insert(enemy_data_list, {enemy_id, filename, x, y, vx, vy, hp, max_hp})
|
2024-05-08 20:33:41 +03:00
|
|
|
::continue::
|
|
|
|
end
|
2024-05-21 10:48:40 +03:00
|
|
|
|
2024-05-30 12:41:56 +03:00
|
|
|
rpc.handle_enemy_data(enemy_data_list)
|
|
|
|
if #dead_entities > 0 then
|
|
|
|
rpc.handle_death_data(dead_entities)
|
|
|
|
end
|
2024-06-02 13:49:11 +03:00
|
|
|
dead_entities = {}
|
2024-05-08 20:33:41 +03:00
|
|
|
end
|
|
|
|
|
|
|
|
function enemy_sync.client_cleanup()
|
2024-05-10 10:30:14 +03:00
|
|
|
local entities = get_sync_entities()
|
|
|
|
for i, enemy_id in ipairs(entities) do
|
2024-05-08 20:33:41 +03:00
|
|
|
if not EntityHasTag(enemy_id, "ew_replicated") then
|
|
|
|
print("Despawning unreplicated "..enemy_id)
|
|
|
|
EntityKill(enemy_id)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
local frame = GameGetFrameNum()
|
2024-05-10 10:30:14 +03:00
|
|
|
for remote_id, enemy_data in pairs(ctx.entity_by_remote_id) do
|
|
|
|
if frame - enemy_data.frame > 60*1 then
|
2024-05-30 11:57:02 +03:00
|
|
|
print("Despawning stale "..remote_id.." "..enemy_data.id)
|
2024-05-08 20:33:41 +03:00
|
|
|
EntityKill(enemy_data.id)
|
2024-05-10 10:30:14 +03:00
|
|
|
ctx.entity_by_remote_id[remote_id] = nil
|
2024-05-08 20:33:41 +03:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2024-05-30 12:41:56 +03:00
|
|
|
function enemy_sync.on_world_update_host()
|
|
|
|
if GameGetFrameNum() % 2 == 1 then
|
|
|
|
enemy_sync.host_upload_entities()
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
function enemy_sync.on_world_update_client()
|
|
|
|
if GameGetFrameNum() % 20 == 1 then
|
|
|
|
enemy_sync.client_cleanup()
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
rpc.opts_reliable()
|
|
|
|
function rpc.handle_death_data(death_data)
|
2024-06-02 13:49:11 +03:00
|
|
|
for _, remote_data in ipairs(death_data) do
|
|
|
|
local remote_id = remote_data[1]
|
|
|
|
local responsible_entity = 0
|
|
|
|
local peer_data = player_fns.peer_get_player_data(remote_data[2], true)
|
|
|
|
if peer_data ~= nil then
|
|
|
|
responsible_entity = peer_data.entity
|
|
|
|
elseif ctx.entity_by_remote_id[remote_data[2]] ~= nil then
|
|
|
|
responsible_entity = ctx.entity_by_remote_id[remote_data[2]]
|
|
|
|
end
|
|
|
|
|
2024-05-19 12:03:03 +03:00
|
|
|
local enemy_data = ctx.entity_by_remote_id[remote_id]
|
|
|
|
if enemy_data ~= nil then
|
|
|
|
local enemy_id = enemy_data.id
|
2024-05-30 11:57:02 +03:00
|
|
|
local immortal = EntityGetFirstComponentIncludingDisabled(enemy_id, "LuaComponent", "ew_immortal")
|
|
|
|
if immortal ~= nil then
|
|
|
|
EntityRemoveComponent(enemy_id, immortal)
|
|
|
|
end
|
|
|
|
local protection_component_id = GameGetGameEffect(enemy_id, "PROTECTION_ALL")
|
2024-06-01 17:10:16 +03:00
|
|
|
if protection_component_id ~= 0 then
|
2024-05-30 11:57:02 +03:00
|
|
|
EntitySetComponentIsEnabled(enemy_id, protection_component_id, false)
|
|
|
|
end
|
2024-05-21 10:48:40 +03:00
|
|
|
|
2024-05-19 12:03:03 +03:00
|
|
|
local current_hp = util.get_ent_health(enemy_id)
|
|
|
|
local dmg = current_hp
|
|
|
|
if dmg > 0 then
|
2024-06-02 13:49:11 +03:00
|
|
|
EntityInflictDamage(enemy_id, dmg+0.1, "DAMAGE_CURSE", "", "NONE", 0, 0, responsible_entity)
|
2024-05-19 12:03:03 +03:00
|
|
|
end
|
2024-06-02 13:49:11 +03:00
|
|
|
EntityInflictDamage(enemy_id, 1000000000, "DAMAGE_CURSE", "", "NONE", 0, 0, responsible_entity) -- Just to be sure
|
2024-05-30 11:57:02 +03:00
|
|
|
EntityKill(enemy_id)
|
2024-05-19 12:03:03 +03:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2024-05-30 12:41:56 +03:00
|
|
|
function rpc.handle_enemy_data(enemy_data)
|
2024-05-08 20:33:41 +03:00
|
|
|
-- 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]
|
2024-05-08 22:13:17 +03:00
|
|
|
local vx = enemy_info_raw[5]
|
|
|
|
local vy = enemy_info_raw[6]
|
|
|
|
local hp = enemy_info_raw[7]
|
|
|
|
local max_hp = enemy_info_raw[8]
|
2024-05-19 12:03:03 +03:00
|
|
|
local has_died = filename == nil
|
2024-05-08 20:33:41 +03:00
|
|
|
|
|
|
|
local frame = GameGetFrameNum()
|
|
|
|
|
2024-05-10 10:30:14 +03:00
|
|
|
if ctx.entity_by_remote_id[remote_enemy_id] ~= nil and not EntityGetIsAlive(ctx.entity_by_remote_id[remote_enemy_id].id) then
|
|
|
|
ctx.entity_by_remote_id[remote_enemy_id] = nil
|
2024-05-08 20:33:41 +03:00
|
|
|
end
|
2024-05-10 22:17:45 +03:00
|
|
|
|
2024-05-10 10:30:14 +03:00
|
|
|
if ctx.entity_by_remote_id[remote_enemy_id] == nil then
|
2024-05-19 12:03:03 +03:00
|
|
|
if filename == nil then
|
|
|
|
goto continue
|
|
|
|
end
|
2024-05-08 20:33:41 +03:00
|
|
|
local enemy_id = EntityLoad(filename, x, y)
|
2024-05-12 18:05:25 +03:00
|
|
|
EntityAddTag(enemy_id, "ew_replicated")
|
2024-05-21 10:48:40 +03:00
|
|
|
EntityAddTag(enemy_id, "polymorphable_NOT")
|
2024-05-30 11:57:02 +03:00
|
|
|
EntityAddComponent2(enemy_id, "LuaComponent", {_tags="ew_immortal", script_damage_about_to_be_received = "mods/quant.ew/files/cbs/immortal.lua"})
|
2024-05-08 22:13:17 +03:00
|
|
|
local ai_component = EntityGetFirstComponentIncludingDisabled(enemy_id, "AnimalAIComponent")
|
2024-05-12 18:05:25 +03:00
|
|
|
if ai_component ~= nil then
|
2024-05-08 22:13:17 +03:00
|
|
|
EntityRemoveComponent(enemy_id, ai_component)
|
|
|
|
end
|
2024-05-10 10:30:14 +03:00
|
|
|
ctx.entity_by_remote_id[remote_enemy_id] = {id = enemy_id, frame = frame}
|
2024-05-08 20:33:41 +03:00
|
|
|
end
|
|
|
|
|
2024-05-10 10:30:14 +03:00
|
|
|
local enemy_data = ctx.entity_by_remote_id[remote_enemy_id]
|
2024-05-08 20:33:41 +03:00
|
|
|
enemy_data.frame = frame
|
|
|
|
local enemy_id = enemy_data.id
|
2024-05-08 22:13:17 +03:00
|
|
|
|
2024-05-19 12:03:03 +03:00
|
|
|
if not has_died then
|
|
|
|
local character_data = EntityGetFirstComponentIncludingDisabled(enemy_id, "CharacterDataComponent")
|
|
|
|
if character_data ~= nil then
|
|
|
|
ComponentSetValue2(character_data, "mVelocity", vx, vy)
|
|
|
|
end
|
|
|
|
local velocity_data = EntityGetFirstComponentIncludingDisabled(enemy_id, "VelocityComponent")
|
|
|
|
if velocity_data ~= nil then
|
|
|
|
ComponentSetValue2(velocity_data, "mVelocity", vx, vy)
|
|
|
|
end
|
2024-05-08 20:33:41 +03:00
|
|
|
EntitySetTransform(enemy_id, x, y)
|
2024-05-19 12:03:03 +03:00
|
|
|
end
|
2024-05-12 18:05:25 +03:00
|
|
|
local current_hp = util.get_ent_health(enemy_id)
|
|
|
|
local dmg = current_hp-hp
|
|
|
|
if dmg > 0 then
|
|
|
|
-- GamePrint("Dealing dmg "..dmg)
|
2024-05-30 23:37:09 +03:00
|
|
|
EntityInflictDamage(enemy_id, dmg, "DAMAGE_CURSE", "", "NONE", 0, 0, ctx.my_player.entity)
|
2024-05-12 18:05:25 +03:00
|
|
|
end
|
2024-05-08 20:33:41 +03:00
|
|
|
util.set_ent_health(enemy_id, {hp, max_hp})
|
2024-05-19 12:03:03 +03:00
|
|
|
::continue::
|
2024-05-08 20:33:41 +03:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
return enemy_sync
|