From 2fc0e76f2c3b7343ae1a1db014fccafcfbae7ddd Mon Sep 17 00:00:00 2001 From: bgkillas Date: Sat, 9 Nov 2024 17:54:58 -0500 Subject: [PATCH] add perma death option, make cessation nicer, fix spectator shields and cursors not appearing sometimes, make default settings transfer nicer --- noita-proxy/assets/lang/en-US/main.ftl | 1 + noita-proxy/src/lib.rs | 248 +++++++++++++----- noita-proxy/src/net.rs | 51 ++-- quant.ew/data/entities/animals/nibbana.xml | 37 +++ .../system/local_health/local_health.lua | 17 +- .../local_health/notplayer/cessation.xml | 13 + .../system/notplayer_ai/notplayer_ai.lua | 35 +-- quant.ew/files/system/player_sync.lua | 9 +- quant.ew/files/system/spectate/spectate.lua | 13 +- .../spectator_helps/spectator_helps.lua | 38 ++- 10 files changed, 331 insertions(+), 131 deletions(-) create mode 100644 quant.ew/data/entities/animals/nibbana.xml create mode 100644 quant.ew/files/system/local_health/notplayer/cessation.xml diff --git a/noita-proxy/assets/lang/en-US/main.ftl b/noita-proxy/assets/lang/en-US/main.ftl index debea008..6e818fe3 100644 --- a/noita-proxy/assets/lang/en-US/main.ftl +++ b/noita-proxy/assets/lang/en-US/main.ftl @@ -99,6 +99,7 @@ local_health_desc_2 = There is a respawn mechanic. Health-percent-lost-on-reviving = HP percent lost on reviving global_hp_loss = Lose HP globally no_material_damage = No material damage +perma_death = Perma death shared_health_desc_1 = Health is shared, but scales with player count. shared_health_desc_2 = Percentage-based damage and full heals are adjusted. shared_health_desc_3 = The original mode. diff --git a/noita-proxy/src/lib.rs b/noita-proxy/src/lib.rs index f15e075a..168f7e9d 100644 --- a/noita-proxy/src/lib.rs +++ b/noita-proxy/src/lib.rs @@ -63,15 +63,39 @@ pub(crate) enum GameMode { // MestariMina, // TODO later } -#[derive(Debug, Decode, Encode, Clone, Serialize, Deserialize, PartialEq)] +#[derive(Debug, Decode, Encode, Clone, Serialize, Deserialize, PartialEq, Default)] #[serde(default)] pub struct GameSettings { seed: u64, + debug_mode: Option, + world_sync_version: Option, + player_tether: Option, + tether_length: Option, + use_constant_seed: bool, + item_dedup: Option, + enemy_hp_mult: Option, + world_sync_interval: Option, + game_mode: Option, + friendly_fire: Option, + friendly_fire_team: Option, + chunk_target: Option, + enemy_sync_interval: Option, + randomize_perks: Option, + progress: Vec, + max_players: Option, + health_per_player: Option, + health_lost_on_revive: Option, + no_material_damage: Option, + global_hp_loss: Option, + perk_ban_list: Option, + perma_death: Option, +} + +pub struct DefaultSettings { debug_mode: bool, world_sync_version: u32, player_tether: bool, tether_length: u32, - use_constant_seed: bool, item_dedup: bool, enemy_hp_mult: f32, world_sync_interval: u32, @@ -81,24 +105,22 @@ pub struct GameSettings { chunk_target: u32, enemy_sync_interval: u32, randomize_perks: bool, - progress: Vec, max_players: u32, health_per_player: u32, health_lost_on_revive: u32, no_material_damage: bool, global_hp_loss: bool, perk_ban_list: String, + perma_death: bool, } -impl Default for GameSettings { +impl Default for DefaultSettings { fn default() -> Self { - GameSettings { - seed: 0, + DefaultSettings { debug_mode: false, world_sync_version: 2, player_tether: true, tether_length: 2048, - use_constant_seed: false, item_dedup: true, randomize_perks: true, enemy_hp_mult: 1.0, @@ -108,13 +130,13 @@ impl Default for GameSettings { friendly_fire_team: 0, chunk_target: 24, enemy_sync_interval: 3, - progress: Vec::new(), max_players: 250, health_per_player: 100, health_lost_on_revive: 0, no_material_damage: false, global_hp_loss: false, - perk_ban_list: String::new(), + perk_ban_list: "GLOBAL_GORE,GLASS_CANNON,REVENGE_RATS,PLAGUE_RATS,VOMIT_RATS,CORDYCEPS,MOLD,FUNGAL_DISEASE,HOMUNCULUS,LUKKI_MINION".to_string(), + perma_death: false } } } @@ -498,7 +520,7 @@ impl App { let peer = net::steam_networking::SteamPeer::new_host( steamworks::LobbyType::Private, self.steam_state.as_ref().unwrap().client.clone(), - self.app_saved_state.game_settings.max_players, + self.app_saved_state.game_settings.max_players.unwrap_or(DefaultSettings::default().max_players), ); let netman = net::NetManager::new(PeerVariant::Steam(peer), self.get_netman_init()); self.set_netman_settings(&netman); @@ -720,56 +742,93 @@ impl App { } fn show_game_settings(&mut self, ui: &mut Ui, show_local: bool) { + let def = DefaultSettings::default(); heading_with_underline(ui, tr("connect_settings")); let game_settings = &mut self.app_saved_state.game_settings; - ui.label(tr("Game-mode")); - ui.radio_value( - &mut game_settings.game_mode, - GameMode::SharedHealth, - tr("Shared-health"), - ); - ui.radio_value( - &mut game_settings.game_mode, - GameMode::LocalHealth, - tr("Local-health"), - ); + { + let mut temp = game_settings.game_mode.unwrap_or(def.game_mode); + ui.label(tr("Game-mode")); + if ui.radio_value( + &mut temp, + GameMode::SharedHealth, + tr("Shared-health"), + ).changed() || ui.radio_value( + &mut temp, + GameMode::LocalHealth, + tr("Local-health"), + ).changed() { + game_settings.game_mode = Some(temp) + } + } ui.scope(|ui| { ui.set_height(100.0); - match game_settings.game_mode { + match game_settings.game_mode.unwrap_or(def.game_mode) { GameMode::SharedHealth => { ui.label(tr("shared_health_desc_1")); ui.label(tr("shared_health_desc_2")); ui.label(tr("shared_health_desc_3")); ui.add_space(5.0); ui.label(tr("Health-per-player")); - ui.add(Slider::new(&mut game_settings.health_per_player, 0..=100)); + let mut temp = game_settings.health_per_player.unwrap_or(def.health_per_player); + if ui.add(Slider::new(&mut temp, 0..=100)).changed() { + game_settings.health_per_player = Some(temp) + } } GameMode::LocalHealth => { ui.label(tr("local_health_desc_1")); ui.label(tr("local_health_desc_2")); ui.add_space(5.0); ui.label(tr("Health-percent-lost-on-reviving")); - ui.add(Slider::new( - &mut game_settings.health_lost_on_revive, - 0..=100, - )); - ui.checkbox(&mut game_settings.global_hp_loss, tr("global_hp_loss")); - ui.checkbox( - &mut game_settings.no_material_damage, - tr("no_material_damage"), - ); + { + let mut temp = game_settings.health_lost_on_revive.unwrap_or(def.health_lost_on_revive); + if ui.add(Slider::new( + &mut temp, + 0..=100, + )).changed() { + game_settings.health_lost_on_revive = Some(temp) + } + } + { + let mut temp = game_settings.global_hp_loss.unwrap_or(def.global_hp_loss); + if ui.checkbox(&mut temp, tr("global_hp_loss")).changed() { + game_settings.global_hp_loss = Some(temp) + } + } + { + let mut temp = game_settings.no_material_damage.unwrap_or(def.no_material_damage); + if ui.checkbox( + &mut temp, + tr("no_material_damage"), + ).changed() { + game_settings.no_material_damage = Some(temp) + } + } + ui.add_space(1.0); + { + let mut temp = game_settings.perma_death.unwrap_or(def.perma_death); + if ui.checkbox( + &mut temp, + tr("perma_death"), + ).changed() { + game_settings.perma_death = Some(temp) + } + } } } }); - ui.add_space(10.0); ui.label(tr("connect_settings_debug")); - ui.checkbox( - &mut game_settings.debug_mode, - tr("connect_settings_debug_en"), - ); + { + let mut temp = game_settings.debug_mode.unwrap_or(def.debug_mode); + if ui.checkbox( + &mut temp, + tr("connect_settings_debug_en"), + ).changed() { + game_settings.debug_mode = Some(temp) + } + } ui.checkbox( &mut game_settings.use_constant_seed, tr("connect_settings_debug_fixed_seed"), @@ -780,40 +839,87 @@ impl App { }); ui.add_space(10.0); ui.label(tr("connect_settings_max_players")); - ui.add(Slider::new(&mut game_settings.max_players, 2..=250)); + { + let mut temp = game_settings.max_players.unwrap_or(def.max_players); + if ui.add(Slider::new(&mut temp, 2..=250)).changed() + { + game_settings.max_players = Some(temp) + } + } ui.add_space(10.0); ui.label(tr("Amount-of-chunks-host-has-loaded-at-once-synced-enemies-and-physics-objects-need-to-be-loaded-in-by-host-to-be-rendered-by-clients")); - ui.add(Slider::new(&mut game_settings.chunk_target, 12..=64)); - + { + let mut temp = game_settings.chunk_target.unwrap_or(def.chunk_target); + if ui.add(Slider::new(&mut temp, 12..=64)).changed() { + game_settings.chunk_target = Some(temp) + } + } ui.add_space(20.0); ui.label(tr("connect_settings_player_tether_desc")); - ui.checkbox( - &mut game_settings.player_tether, - tr("connect_settings_player_tether"), - ); - ui.add( - Slider::new(&mut game_settings.tether_length, 10..=5000) - .text(tr("connect_settings_player_tether_length")), - ); + { + let mut temp = game_settings.player_tether.unwrap_or(def.player_tether); + if ui.checkbox( + &mut temp, + tr("connect_settings_player_tether"), + ).changed() + { + game_settings.player_tether = Some(temp) + } + } + { + let mut temp = game_settings.tether_length.unwrap_or(def.tether_length); + if ui.add( + Slider::new(&mut temp, 10..=5000) + .text(tr("connect_settings_player_tether_length")), + ).changed() { + game_settings.tether_length = Some(temp) + } + } ui.add_space(20.0); - ui.checkbox( - &mut game_settings.item_dedup, - tr("connect_settings_item_dedup"), - ); - ui.checkbox( - &mut game_settings.randomize_perks, - tr("Have-perk-pools-be-independent-of-each-other"), - ); - ui.horizontal(|ui| { - ui.text_edit_singleline(&mut game_settings.perk_ban_list); - ui.label("perk ban list, comma seperated"); - }); - ui.add( - Slider::new(&mut game_settings.enemy_hp_mult, 1.0..=1000.0) - .logarithmic(true) - .text(tr("connect_settings_enemy_hp_scale")), - ); - ui.checkbox(&mut game_settings.friendly_fire, tr("Enable-friendly-fire")); + { + let mut temp = game_settings.item_dedup.unwrap_or(def.item_dedup); + if ui.checkbox( + &mut temp, + tr("connect_settings_item_dedup"), + ).changed() { + game_settings.item_dedup = Some(temp) + } + } + { + let mut temp = game_settings.randomize_perks.unwrap_or(def.randomize_perks); + if ui.checkbox( + &mut temp, + tr("Have-perk-pools-be-independent-of-each-other"), + ).changed() { + game_settings.randomize_perks = Some(temp) + } + } + { + let mut temp = game_settings.perk_ban_list.clone().unwrap_or(def.perk_ban_list); + ui.horizontal(|ui| { + if ui.text_edit_singleline(&mut temp) + .changed() { + game_settings.perk_ban_list = Some(temp) + } + ui.label("perk ban list, comma seperated"); + }); + } + { + let mut temp = game_settings.enemy_hp_mult.unwrap_or(def.enemy_hp_mult); + if ui.add( + Slider::new(&mut temp, 1.0..=1000.0) + .logarithmic(true) + .text(tr("connect_settings_enemy_hp_scale")), + ).changed() { + game_settings.enemy_hp_mult = Some(temp) + } + } + { + let mut temp = game_settings.friendly_fire.unwrap_or(def.friendly_fire); + if ui.checkbox(&mut temp, tr("Enable-friendly-fire")).changed() { + game_settings.friendly_fire = Some(temp) + } + } if show_local { heading_with_underline(ui, tr("connect_settings_local")); ui.checkbox( @@ -1097,10 +1203,14 @@ impl eframe::App for App { ui.add_space(15.0); if netman.friendly_fire.load(Ordering::Relaxed) { let last = self.app_saved_state.game_settings.friendly_fire_team; - ui.add(Slider::new(&mut self.app_saved_state.game_settings.friendly_fire_team, -1..=16)); + let def = DefaultSettings::default(); + let mut temp = self.app_saved_state.game_settings.friendly_fire_team.unwrap_or(def.friendly_fire_team); + if ui.add(Slider::new(&mut temp, -1..=16)).changed() { + self.app_saved_state.game_settings.friendly_fire_team = Some(temp); + } if last != self.app_saved_state.game_settings.friendly_fire_team || netman.friendly_fire_team.load(Ordering::Relaxed) == -2 { - netman.friendly_fire_team.store(self.app_saved_state.game_settings.friendly_fire_team, Ordering::Relaxed); + netman.friendly_fire_team.store(temp, Ordering::Relaxed); } ui.label("what team number you are on, 0 means no team, -1 means friendly"); ui.add_space(15.0); @@ -1462,4 +1572,4 @@ pub fn host_cli(port: u16) { let player_path = netmaninit.player_path.clone(); let netman = net::NetManager::new(varient, netmaninit); netman.start_inner(player_path, true).unwrap(); -} +} \ No newline at end of file diff --git a/noita-proxy/src/net.rs b/noita-proxy/src/net.rs index 94a2a1fd..1e0c7ba8 100644 --- a/noita-proxy/src/net.rs +++ b/noita-proxy/src/net.rs @@ -26,11 +26,7 @@ use tungstenite::{accept, WebSocket}; use crate::mod_manager::ModmanagerSettings; use crate::player_cosmetics::{create_player_png, PlayerPngDesc}; -use crate::{ - bookkeeping::save_state::{SaveState, SaveStateEntry}, - recorder::Recorder, - GameSettings, PlayerColor, -}; +use crate::{bookkeeping::save_state::{SaveState, SaveStateEntry}, recorder::Recorder, DefaultSettings, GameSettings, PlayerColor}; pub mod messages; mod proxy_opt; pub mod steam_networking; @@ -133,6 +129,7 @@ pub struct NetManager { pub friendly_fire: AtomicBool, pub ban_list: Mutex>, pub kick_list: Mutex>, + pub no_more_players: AtomicBool, } impl NetManager { @@ -155,6 +152,7 @@ impl NetManager { friendly_fire: AtomicBool::new(false), ban_list: Default::default(), kick_list: Default::default(), + no_more_players: AtomicBool::new(false), } .into() } @@ -472,6 +470,7 @@ impl NetManager { .expect("can set write timeout"); let settings = self.settings.lock().unwrap(); + let def = DefaultSettings::default(); state.try_ws_write(ws_encode_proxy("seed", settings.seed)); let my_id = self.peer.my_id(); state.try_ws_write(ws_encode_proxy("peer_id", format!("{:016x}", my_id.0))); @@ -485,25 +484,27 @@ impl NetManager { } else { info!("No nickname chosen"); } - self.friendly_fire - .store(settings.friendly_fire, atomic::Ordering::Relaxed); - state.try_ws_write_option("friendly_fire", settings.friendly_fire); - state.try_ws_write_option("debug", settings.debug_mode); - state.try_ws_write_option("world_sync_version", settings.world_sync_version); - state.try_ws_write_option("player_tether", settings.player_tether); - state.try_ws_write_option("tether_length", settings.tether_length); - state.try_ws_write_option("item_dedup", settings.item_dedup); - state.try_ws_write_option("randomize_perks", settings.randomize_perks); - state.try_ws_write_option("enemy_hp_scale", settings.enemy_hp_mult); - state.try_ws_write_option("world_sync_interval", settings.world_sync_interval); - state.try_ws_write_option("game_mode", settings.game_mode); - state.try_ws_write_option("chunk_target", settings.chunk_target); - state.try_ws_write_option("health_per_player", settings.health_per_player); - state.try_ws_write_option("enemy_sync_interval", settings.enemy_sync_interval); - state.try_ws_write_option("global_hp_loss", settings.global_hp_loss); - state.try_ws_write_option("perk_ban_list", settings.perk_ban_list.as_str()); - state.try_ws_write_option("no_material_damage", settings.no_material_damage); - state.try_ws_write_option("health_lost_on_revive", settings.health_lost_on_revive); + let ff = settings.friendly_fire.unwrap_or(def.friendly_fire); + self.friendly_fire.store(ff, atomic::Ordering::Relaxed); + state.try_ws_write_option("friendly_fire", ff); + state.try_ws_write_option("debug", settings.debug_mode.unwrap_or(def.debug_mode)); + state.try_ws_write_option("world_sync_version", settings.world_sync_version.unwrap_or(def.world_sync_version)); + state.try_ws_write_option("player_tether", settings.player_tether.unwrap_or(def.player_tether)); + state.try_ws_write_option("tether_length", settings.tether_length.unwrap_or(def.tether_length)); + state.try_ws_write_option("item_dedup", settings.item_dedup.unwrap_or(def.item_dedup)); + state.try_ws_write_option("randomize_perks", settings.randomize_perks.unwrap_or(def.randomize_perks)); + state.try_ws_write_option("enemy_hp_scale", settings.enemy_hp_mult.unwrap_or(def.enemy_hp_mult)); + state.try_ws_write_option("world_sync_interval", settings.world_sync_interval.unwrap_or(def.world_sync_interval)); + state.try_ws_write_option("game_mode", settings.game_mode.unwrap_or(def.game_mode)); + state.try_ws_write_option("chunk_target", settings.chunk_target.unwrap_or(def.chunk_target)); + state.try_ws_write_option("health_per_player", settings.health_per_player.unwrap_or(def.health_per_player)); + state.try_ws_write_option("enemy_sync_interval", settings.enemy_sync_interval.unwrap_or(def.enemy_sync_interval)); + state.try_ws_write_option("global_hp_loss", settings.global_hp_loss.unwrap_or(def.global_hp_loss)); + state.try_ws_write_option("perma_death", settings.perma_death.unwrap_or(def.perma_death)); + let lst = settings.clone(); + state.try_ws_write_option("perk_ban_list", lst.perk_ban_list.unwrap_or(def.perk_ban_list).as_str()); + state.try_ws_write_option("no_material_damage", settings.no_material_damage.unwrap_or(def.no_material_damage)); + state.try_ws_write_option("health_lost_on_revive", settings.health_lost_on_revive.unwrap_or(def.health_lost_on_revive)); let rgb = self.init_settings.player_color.player_main; state.try_ws_write_option( "mina_color", @@ -670,4 +671,4 @@ impl Drop for NetManager { info!("Skip saving run info: not a host"); } } -} +} \ No newline at end of file diff --git a/quant.ew/data/entities/animals/nibbana.xml b/quant.ew/data/entities/animals/nibbana.xml new file mode 100644 index 00000000..e7bbefa6 --- /dev/null +++ b/quant.ew/data/entities/animals/nibbana.xml @@ -0,0 +1,37 @@ + + + + + + + \ No newline at end of file diff --git a/quant.ew/files/system/local_health/local_health.lua b/quant.ew/files/system/local_health/local_health.lua index 09feb288..99ee0497 100644 --- a/quant.ew/files/system/local_health/local_health.lua +++ b/quant.ew/files/system/local_health/local_health.lua @@ -176,9 +176,22 @@ local function player_died() -- This may look like a hack, but it allows to use existing poly machinery to change player entity AND to store the original player for later, -- Which is, like, perfect. + GameAddFlagRun("ew_flag_notplayer_active") + if ctx.proxy_opt.perma_death then + local ent = LoadGameEffectEntityTo(ctx.my_player.entity, "mods/quant.ew/files/system/local_health/notplayer/cessation.xml") + polymorph.switch_entity(ent + 1) + GameAddFlagRun("msg_gods_looking") + GameAddFlagRun("msg_gods_looking2") + EntityAddTag(ctx.my_player.entity, "ew_notplayer") + for _, child in ipairs(EntityGetAllChildren(ctx.my_player.entity) or {}) do + if EntityGetName(child) == "cursor" then + EntitySetComponentIsEnabled(child, EntityGetFirstComponentIncludingDisabled(child, "SpriteComponent"), false) + end + end + return + end local ent = LoadGameEffectEntityTo(ctx.my_player.entity, "mods/quant.ew/files/system/local_health/notplayer/poly_effect.xml") ctx.my_player.entity = ent + 1 - GameAddFlagRun("ew_flag_notplayer_active") do_switch_effect(false) EntitySetName(ctx.my_player.entity, ctx.my_id.."?") util.set_ent_health(ctx.my_player.entity, {max_hp, max_hp}) @@ -191,7 +204,7 @@ local function player_died() remove_healthbar_locally() for _, child in ipairs(EntityGetAllChildren(ctx.my_player.entity) or {}) do if EntityGetName(child) == "cursor" or EntityGetName(child) == "notcursor" then - EntityKill(child) + EntitySetComponentIsEnabled(child, EntityGetFirstComponentIncludingDisabled(child, "SpriteComponent"), false) end end inventory_helper.set_item_data(item_data, ctx.my_player) diff --git a/quant.ew/files/system/local_health/notplayer/cessation.xml b/quant.ew/files/system/local_health/notplayer/cessation.xml new file mode 100644 index 00000000..19fd0a8d --- /dev/null +++ b/quant.ew/files/system/local_health/notplayer/cessation.xml @@ -0,0 +1,13 @@ + + + + + + + + diff --git a/quant.ew/files/system/notplayer_ai/notplayer_ai.lua b/quant.ew/files/system/notplayer_ai/notplayer_ai.lua index 8c7bcc64..563a90b8 100644 --- a/quant.ew/files/system/notplayer_ai/notplayer_ai.lua +++ b/quant.ew/files/system/notplayer_ai/notplayer_ai.lua @@ -637,6 +637,8 @@ local function choose_movement() ComponentSetValue2(state.control_component, "mButtonFrameRight", GameGetFrameNum()+100) return end + local air = ComponentGetValue2(state.damage_model, "air_in_lungs") + local fly = ComponentGetValue2(state.data_component, "mFlyingTimeLeft") if state.target == nil or (has_ambrosia(ctx.my_player.entity) and state.init_timer > no_shoot_time + 4) then state.control_a = false state.control_d = false @@ -663,13 +665,14 @@ local function choose_movement() move = -1 end - local air = ComponentGetValue2(state.damage_model, "air_in_lungs") - if air < 1 then - state.control_w = true - state.control_s = false - elseif air < 2 then - state.control_s = true - state.control_w = false + if air < 2 then + if fly < 0.5 then + state.control_s = true + state.control_w = false + else + state.control_w = true + state.control_s = false + end end return end @@ -752,7 +755,7 @@ local function choose_movement() on_right = my_x > t_x end - if ComponentGetValue2(state.data_component, "mFlyingTimeLeft") < 0.2 and GameGetFrameNum() % 300 > 250 then + if fly < 0.2 and GameGetFrameNum() % 300 > 250 then rest = true give_space = give_space + 10 swap_side = false @@ -863,13 +866,14 @@ local function choose_movement() if did_hit_up and state.water_potion ~= nil then state.control_w = false end - local air = ComponentGetValue2(state.damage_model, "air_in_lungs") - if air < 1 then - state.control_w = true - state.control_s = false - elseif air < 2 then - state.control_s = true - state.control_w = false + if air < 2 then + if fly < 0.5 then + state.control_s = true + state.control_w = false + else + state.control_w = true + state.control_s = false + end end end @@ -1441,6 +1445,7 @@ end function module.on_world_update() local active = GameHasFlagRun("ew_flag_notplayer_active") + and not ctx.proxy_opt.perma_death if active and EntityGetIsAlive(ctx.my_player.entity) and EntityHasTag(ctx.my_player.entity, "ew_notplayer") then if state == nil then init_state() diff --git a/quant.ew/files/system/player_sync.lua b/quant.ew/files/system/player_sync.lua index a1190409..1133c7a8 100644 --- a/quant.ew/files/system/player_sync.lua +++ b/quant.ew/files/system/player_sync.lua @@ -74,8 +74,15 @@ function module.on_world_update() local mx, my = GameGetCameraPos() for peer_id, player in pairs(ctx.players) do local ent = player.entity + local children = EntityGetAllChildren(ent) or {} + for _, child in ipairs(children) do + if EntityGetName(child) == "cursor" or EntityGetName(child) == "notcursor" then + EntitySetComponentIsEnabled(child, EntityGetFirstComponentIncludingDisabled(child, "SpriteComponent"), true) + end + end local x, y = EntityGetTransform(ent) local notplayer = EntityHasTag(ent, "ew_notplayer") + and not ctx.proxy_opt.perma_death if notplayer and GameHasFlagRun("ending_game_completed") then goto continue end @@ -84,7 +91,7 @@ function module.on_world_update() end local dx, dy = x - mx, y - my local cape - for _, child in ipairs(EntityGetAllChildren(ent) or {}) do + for _, child in ipairs(children) do if EntityGetName(child) == "cape" then local cpe = EntityGetFirstComponentIncludingDisabled(child, "VerletPhysicsComponent") local cx, cy = ComponentGetValue2(cpe, "m_position_previous") diff --git a/quant.ew/files/system/spectate/spectate.lua b/quant.ew/files/system/spectate/spectate.lua index 77ae43a6..768e6845 100644 --- a/quant.ew/files/system/spectate/spectate.lua +++ b/quant.ew/files/system/spectate/spectate.lua @@ -72,7 +72,7 @@ local function get_me() local i = 0 local alive = -1, -1 for peer_id, potential_target in pairs(ctx.players) do - if GameHasFlagRun("ending_game_completed") and EntityHasTag(potential_target.entity, "ew_notplayer") then + if (GameHasFlagRun("ending_game_completed") or ctx.proxy_opt.perma_death) and EntityHasTag(potential_target.entity, "ew_notplayer") then goto continue end i = i + 1 @@ -218,7 +218,7 @@ local function set_camera_pos() if cam_target == nil or re_cam then local i = 0 for peer_id, potential_target in pairs(ctx.players) do - if GameHasFlagRun("ending_game_completed") and EntityHasTag(potential_target.entity, "ew_notplayer") then + if (GameHasFlagRun("ending_game_completed") or ctx.proxy_opt.perma_death) and EntityHasTag(potential_target.entity, "ew_notplayer") then goto continue end i = i + 1 @@ -246,7 +246,8 @@ end local function update_i() local i = 0 for peer_id, potential_target in pairs(ctx.players) do - if GameHasFlagRun("ending_game_completed") and EntityHasTag(potential_target.entity, "ew_notplayer") then + if (GameHasFlagRun("ending_game_completed") or ctx.proxy_opt.perma_death) + and EntityHasTag(potential_target.entity, "ew_notplayer") then goto continue end i = i + 1 @@ -263,7 +264,7 @@ end local function number_of_players() local i = 0 for _, potential_target in pairs(ctx.players) do - if GameHasFlagRun("ending_game_completed") and EntityHasTag(potential_target.entity, "ew_notplayer") then + if (GameHasFlagRun("ending_game_completed") or ctx.proxy_opt.perma_death) and EntityHasTag(potential_target.entity, "ew_notplayer") then goto continue end i = i + 1 @@ -296,7 +297,9 @@ function spectate.on_world_update() update_i() last_len = number_of_players() end - if cam_target ~= nil and GameHasFlagRun("ending_game_completed") and EntityHasTag(cam_target.entity, "ew_notplayer") then + if cam_target ~= nil + and ((GameHasFlagRun("ending_game_completed") or ctx.proxy_opt.perma_death) + and EntityHasTag(cam_target.entity, "ew_notplayer")) then update_i() last_len = number_of_players() end diff --git a/quant.ew/files/system/spectator_helps/spectator_helps.lua b/quant.ew/files/system/spectator_helps/spectator_helps.lua index cb6ac6a1..d16bea13 100644 --- a/quant.ew/files/system/spectator_helps/spectator_helps.lua +++ b/quant.ew/files/system/spectator_helps/spectator_helps.lua @@ -2,6 +2,15 @@ local rpc = net.new_rpc_namespace() local shield_entities = {} +local function remove_shield(peer_id) + if shield_entities[peer_id] ~= nil then + if EntityGetIsAlive(shield_entities[peer_id][2]) then + EntityKill(shield_entities[peer_id][2]) + end + shield_entities[peer_id] = nil + end +end + rpc.opts_everywhere() rpc.opts_reliable() function rpc.add_shield(target) @@ -12,23 +21,21 @@ function rpc.add_shield(target) if not EntityGetIsAlive(entity) or EntityHasTag(entity, "polymorphed") then return end - if shield_entities[ctx.rpc_peer_id] ~= nil then - EntityKill(shield_entities[ctx.rpc_peer_id][2]) + if shield_entities[ctx.rpc_peer_id] == nil or shield_entities[ctx.rpc_peer_id][1] ~= target then + if shield_entities[ctx.rpc_peer_id] ~= nil + and EntityGetIsAlive(shield_entities[ctx.rpc_peer_id][2]) then + EntityKill(shield_entities[ctx.rpc_peer_id][2]) + end + local ent = EntityLoad("mods/quant.ew/files/system/spectator_helps/shield_base.xml") + EntityAddChild(entity, ent) + shield_entities[ctx.rpc_peer_id] = {target, ent} end - local ent = EntityLoad("mods/quant.ew/files/system/spectator_helps/shield_base.xml") - EntityAddChild(entity, ent) - shield_entities[ctx.rpc_peer_id] = {target, ent} end rpc.opts_everywhere() rpc.opts_reliable() function rpc.del_shield() - if shield_entities[ctx.rpc_peer_id] ~= nil then - if EntityGetIsAlive(shield_entities[ctx.rpc_peer_id][2]) then - EntityKill(shield_entities[ctx.rpc_peer_id][2]) - end - shield_entities[ctx.rpc_peer_id] = nil - end + remove_shield(ctx.rpc_peer_id) end local module = {} @@ -48,11 +55,11 @@ local function is_acceptable_help_target(spectating_over) return false end if shield_entities[ctx.my_id] ~= nil then - if shield_entities[ctx.my_id][1] ~= spectating_over then + if shield_entities[ctx.my_id][1] ~= spectating_over then rpc.del_shield() return false end - return false + return GameGetFrameNum() % 300 < 10 end return true end @@ -77,7 +84,10 @@ end local last_spectate function module.on_world_update() - if GameHasFlagRun("ending_game_completed") then + if GameHasFlagRun("ending_game_completed") or ctx.proxy_opt.perma_death then + if not ctx.proxy_opt.perma_death then + rpc.del_shield() + end return end if GameGetFrameNum() % 10 == 8 then