diff --git a/noita-proxy/assets/notplayer.png b/noita-proxy/assets/notplayer.png new file mode 100644 index 00000000..4afe4847 Binary files /dev/null and b/noita-proxy/assets/notplayer.png differ diff --git a/noita-proxy/notplayer.png b/noita-proxy/notplayer.png new file mode 100644 index 00000000..4afe4847 Binary files /dev/null and b/noita-proxy/notplayer.png differ diff --git a/noita-proxy/src/lib.rs b/noita-proxy/src/lib.rs index 26acdad2..8c0f266e 100644 --- a/noita-proxy/src/lib.rs +++ b/noita-proxy/src/lib.rs @@ -6,11 +6,12 @@ use bookkeeping::{ }; use clipboard::{ClipboardContext, ClipboardProvider}; use cpal::traits::{DeviceTrait, HostTrait}; +use eframe::egui::load::TexturePoll; use eframe::egui::{ self, Align2, Button, Color32, ComboBox, Context, DragValue, FontDefinitions, FontFamily, ImageButton, InnerResponse, Key, Layout, Margin, OpenUrl, OutputCommand, Rect, RichText, - ScrollArea, Sense, Slider, TextureOptions, ThemePreference, Ui, UiBuilder, Vec2, Visuals, - Window, pos2, + ScrollArea, Sense, SizeHint, Slider, TextureOptions, ThemePreference, Ui, UiBuilder, Vec2, + Visuals, Window, pos2, }; use eframe::epaint::TextureHandle; use image::DynamicImage::ImageRgba8; @@ -844,6 +845,18 @@ impl Default for PlayerColor { } } } +/*impl PlayerColor { + pub fn notplayer() -> Self { + Self { + player_main: [155.0, 111.0, 154.0, 255.0], + player_alt: [127.0, 84.0, 118.0, 255.0], + player_arm: [89.0, 67.0, 84.0, 255.0], + player_cape: [118.0, 84.0, 127.0, 255.0], + player_cape_edge: [154.0, 111.0, 155.0, 255.0], + player_forearm: [158.0, 115.0, 154.0, 255.0], + } + } +}*/ #[derive(Debug, Serialize, Deserialize, PartialEq, Clone)] enum PlayerPicker { None, @@ -1025,7 +1038,8 @@ struct ImageMap { textures: FxHashMap, zoom: f32, offset: Vec2, - players: FxHashMap, TextureHandle)>, + players: FxHashMap, bool, bool, TextureHandle)>, + notplayer: Option, centered_on: Option, dont_scale: bool, } @@ -1036,6 +1050,7 @@ impl Default for ImageMap { zoom: 1.0, offset: Vec2::new(f32::MAX, f32::MAX), players: Default::default(), + notplayer: None, centered_on: None, dont_scale: false, } @@ -1065,9 +1080,9 @@ impl ImageMap { fn update_player_textures( &mut self, ui: &mut Ui, - map: &FxHashMap, RgbaImage)>, + map: &FxHashMap, bool, bool, RgbaImage)>, ) { - for (p, (coord, img)) in map { + for (p, (coord, is_dead, does_exist, img)) in map { if !self.players.contains_key(p) { let name = format!("{}", p); let size = [img.width() as usize, img.height() as usize]; @@ -1078,9 +1093,14 @@ impl ImageMap { let tex = ui .ctx() .load_texture(name, color_image, TextureOptions::NEAREST); - self.players.insert(*p, (*coord, tex)); + self.players + .insert(*p, (*coord, *is_dead, *does_exist, tex)); } - self.players.entry(*p).and_modify(|(w, _)| *w = *coord); + self.players.entry(*p).and_modify(|(w, b, d, _)| { + *w = *coord; + *b = *is_dead; + *d = *does_exist; + }); } } fn ui(&mut self, ui: &mut Ui, netman: &NetManStopOnDrop, ctx: &Context) { @@ -1089,7 +1109,7 @@ impl ImageMap { } if netman.reset_map.load(Ordering::Relaxed) { netman.reset_map.store(false, Ordering::Relaxed); - self.textures.clear() + self.textures.clear(); } { let map = &mut netman.chunk_map.lock().unwrap(); @@ -1098,6 +1118,11 @@ impl ImageMap { } map.clear(); } + if self.notplayer.is_none() { + self.notplayer = egui::include_image!("../assets/notplayer.png") + .load(ctx, TextureOptions::NEAREST, SizeHint::Size(7, 16)) + .ok(); + } self.update_player_textures(ui, &netman.players_sprite.lock().unwrap()); let response = ui.interact( ui.available_rect_before_wrap(), @@ -1123,10 +1148,10 @@ impl ImageMap { self.offset.y -= 16.0 } if ui.input(|i| i.keys_down.contains(&Key::D) || i.keys_down.contains(&Key::ArrowRight)) { - self.offset.x += 16.0 + self.offset.x -= 16.0 } if ui.input(|i| i.keys_down.contains(&Key::A) || i.keys_down.contains(&Key::ArrowLeft)) { - self.offset.x -= 16.0 + self.offset.x += 16.0 } if ui.input(|i| i.keys_down.contains(&Key::X)) { self.dont_scale = !self.dont_scale @@ -1137,7 +1162,7 @@ impl ImageMap { let players: Vec = self .players .iter() - .filter_map(|(a, (c, _))| if c.is_some() { Some(a) } else { None }) + .filter_map(|(a, (c, _, d, _))| if c.is_some() && !d { Some(a) } else { None }) .cloned() .collect(); if !players.is_empty() { @@ -1156,6 +1181,15 @@ impl ImageMap { } } let mut tile_size = self.zoom * 128.0; + if let Some(peer) = self.centered_on { + if let Some((Some(pos), _, _, _)) = self.players.get(&peer) { + self.offset = Vec2::new(ui.available_width() / 2.0, ui.available_height() / 2.0) + - Vec2::new( + pos.x as f32 * tile_size / 128.0, + (pos.y - 13) as f32 * tile_size / 128.0, + ) + } + } let painter = ui.painter(); for (coord, tex) in &self.textures { let pos = @@ -1168,7 +1202,10 @@ impl ImageMap { Color32::WHITE, ); } - for (pos, tex) in self.players.values() { + for (pos, is_dead, does_exist, tex) in self.players.values() { + if !does_exist { + continue; + } if let Some(pos) = pos { if self.dont_scale { tile_size = 128.0 @@ -1182,12 +1219,25 @@ impl ImageMap { pos.to_pos2(), Vec2::new(8.0 * tile_size / 128.0, 18.0 * tile_size / 128.0), ); - painter.image( - tex.id(), - rect, - Rect::from_min_max(pos2(0.0, 0.0), pos2(1.0, 1.0)), - Color32::WHITE, - ); + if *is_dead { + if let Some(tex) = &self.notplayer { + if let Some(id) = tex.texture_id() { + painter.image( + id, + rect, + Rect::from_min_max(pos2(0.0, 0.0), pos2(1.0, 1.0)), + Color32::WHITE, + ); + } + } + } else { + painter.image( + tex.id(), + rect, + Rect::from_min_max(pos2(0.0, 0.0), pos2(1.0, 1.0)), + Color32::WHITE, + ); + } } } } @@ -1606,7 +1656,7 @@ impl App { if self.app_saved_state.times_started % 20 == 0 { let image = egui::Image::new(egui::include_image!("../assets/longleg.png")) .texture_options(TextureOptions::NEAREST); - image.paint_at(ui, ui.ctx().screen_rect()); + image.paint_at(ui, ctx.screen_rect()); } else { draw_bg(ui); } diff --git a/noita-proxy/src/net.rs b/noita-proxy/src/net.rs index 220d3362..52e4885c 100644 --- a/noita-proxy/src/net.rs +++ b/noita-proxy/src/net.rs @@ -209,7 +209,8 @@ pub struct NetManager { duplicate: AtomicBool, pub back_out: AtomicBool, pub chunk_map: Mutex>, - pub players_sprite: Mutex, RgbaImage)>>, + #[allow(clippy::type_complexity)] + pub players_sprite: Mutex, bool, bool, RgbaImage)>>, pub reset_map: AtomicBool, colors: Mutex>, } @@ -735,10 +736,13 @@ impl NetManager { .play_audio(audio, pos, src, data, global, (tx, ty), vol); } } - NetMsg::PlayerPosition(x, y) => { + NetMsg::PlayerPosition(x, y, is_dead, does_exist) => { let map = &mut self.players_sprite.lock().unwrap(); - map.entry(src) - .and_modify(|(w, _)| *w = Some(WorldPos::from((x, y)))); + map.entry(src).and_modify(|(w, b, d, _)| { + *w = Some(WorldPos::from((x, y))); + *b = is_dead; + *d = does_exist + }); } NetMsg::MapData(chunks) => { for (ch, c) in chunks { @@ -1245,13 +1249,21 @@ impl NetManager { } let x: Option = msg.next().and_then(|s| s.parse().ok()); let y: Option = msg.next().and_then(|s| s.parse().ok()); + let b: bool = msg + .next() + .map(|s| s.parse().ok() == Some(1)) + .unwrap_or(false); + let d: bool = msg + .next() + .map(|s| s.parse().ok() == Some(1)) + .unwrap_or(false); if let (Some(x), Some(y)) = (x, y) { self.player_pos.0.store(x, Ordering::Relaxed); self.player_pos.1.store(y, Ordering::Relaxed); - self.broadcast(&NetMsg::PlayerPosition(x, y), Reliability::Reliable); + self.broadcast(&NetMsg::PlayerPosition(x, y, b, d), Reliability::Reliable); self.send( self.peer.my_id(), - &NetMsg::PlayerPosition(x, y), + &NetMsg::PlayerPosition(x, y, b, d), Reliability::Reliable, ); } diff --git a/noita-proxy/src/net/messages.rs b/noita-proxy/src/net/messages.rs index 4d54424b..9c859128 100644 --- a/noita-proxy/src/net/messages.rs +++ b/noita-proxy/src/net/messages.rs @@ -33,7 +33,7 @@ pub(crate) enum NetMsg { RespondFlagNormal(String, bool), RespondFlagSlow(usize, bool), RespondFlagMoon(i32, i32, bool), - PlayerPosition(i32, i32), + PlayerPosition(i32, i32, bool, bool), RespondFlagStevari(i32, i32, OmniPeerId), AudioData(Vec>, bool, i32, i32, f32), MapData(FxHashMap), diff --git a/noita-proxy/src/player_cosmetics.rs b/noita-proxy/src/player_cosmetics.rs index c22922c2..e14582d7 100644 --- a/noita-proxy/src/player_cosmetics.rs +++ b/noita-proxy/src/player_cosmetics.rs @@ -368,13 +368,14 @@ fn replace_colors_opt(path: PathBuf, save: PathBuf, rgb: &PlayerColor, inv: bool img.save(save).unwrap(); } +#[allow(clippy::type_complexity)] pub fn create_player_png( peer: OmniPeerId, mod_path: &Path, player_path: &Path, rgb: &PlayerPngDesc, is_host: bool, - player_map: &mut MutexGuard, RgbaImage)>>, + player_map: &mut MutexGuard, bool, bool, RgbaImage)>>, ) { let icon = get_player_skin( image::open(player_path) @@ -383,7 +384,7 @@ pub fn create_player_png( .into_rgba8(), *rgb, ); - player_map.insert(peer, (None, icon.clone())); + player_map.insert(peer, (None, false, false, icon.clone())); let inv = rgb.invert_border; let id = peer.as_hex(); let cosmetics = rgb.cosmetics; diff --git a/quant.ew/files/system/proxy_info.lua b/quant.ew/files/system/proxy_info.lua index b086ab89..dc84bd80 100644 --- a/quant.ew/files/system/proxy_info.lua +++ b/quant.ew/files/system/proxy_info.lua @@ -42,6 +42,14 @@ function module.on_world_update() if cess then c = 1 end + local is_dead = 0 + if GameHasFlagRun("ew_flag_notplayer_active") then + is_dead = 1 + end + local d = 0 + if ctx.proxy_opt.no_notplayer or ctx.proxy_opt.perma_death then + d = 1 + end net.proxy_send( "cam_pos", math.floor(x) @@ -52,6 +60,10 @@ function module.on_world_update() .. " " .. math.floor(my) .. " " + .. is_dead + .. " " + .. d + .. " " .. ptt .. " " .. a