-
Notifications
You must be signed in to change notification settings - Fork 55
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Quality of Life Improvements: Bomb Info + Misc #64
base: master
Are you sure you want to change the base?
Changes from all commits
31f9a28
617ddcc
59f33c3
05c3df6
666448f
13583b2
880bf5f
0d076f8
133a0af
07e64d2
e2d09a5
f631f0a
6087aaf
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,12 +2,19 @@ use std::ffi::CStr; | |
|
||
use anyhow::Context; | ||
use cs2::CEntityIdentityEx; | ||
use cs2_schema_generated::cs2::client::C_PlantedC4; | ||
use cs2_schema_generated::cs2::client::{ | ||
CCSPlayer_ItemServices, | ||
C_CSGameRulesProxy, | ||
C_CSPlayerPawn, | ||
C_PlantedC4, | ||
}; | ||
use imgui::Condition; | ||
use mint::Vector4; | ||
use obfstr::obfstr; | ||
|
||
use super::Enhancement; | ||
use crate::{ | ||
utils::ImguiUiEx, | ||
RenderContext, | ||
UpdateContext, | ||
}; | ||
|
||
|
@@ -46,15 +53,29 @@ pub enum C4State { | |
|
||
/// Bomb has been defused | ||
Defused, | ||
|
||
/// Bomb has been dropped | ||
Dropped, | ||
} | ||
|
||
pub struct BombInfo { | ||
/// Number of Counter-Terrorists with kit | ||
any_kit: bool, | ||
|
||
/// Local Team Identifier | ||
local_team: u8, | ||
|
||
/// Current State of Bomb | ||
bomb_state: Option<C4Info>, | ||
} | ||
|
||
impl BombInfo { | ||
pub fn new() -> Self { | ||
Self { bomb_state: None } | ||
Self { | ||
any_kit: false, | ||
local_team: 0, | ||
bomb_state: None, | ||
} | ||
} | ||
|
||
fn read_state(&self, ctx: &UpdateContext) -> anyhow::Result<Option<C4Info>> { | ||
|
@@ -66,6 +87,31 @@ impl BombInfo { | |
.lookup(&entity_identity.entity_class_info()?) | ||
.context("class name")?; | ||
|
||
// Check if bomb is dropped | ||
if class_name | ||
.map(|name| name == "C_CSGameRulesProxy") | ||
.unwrap_or(false) | ||
{ | ||
/* The bomb is dropped. */ | ||
let rules_proxy = entity_identity | ||
.entity_ptr::<C_CSGameRulesProxy>()? | ||
.reference_schema() | ||
.context("rules proxy missing")?; | ||
|
||
let game_rules = rules_proxy | ||
.m_pGameRules()? | ||
.reference_schema() | ||
.context("game rules missing")?; | ||
(); | ||
|
||
if game_rules.m_bBombDropped().unwrap_or_default() { | ||
return Ok(Some(C4Info { | ||
bomb_site: 0, | ||
state: C4State::Dropped, | ||
})); | ||
} | ||
} | ||
|
||
if !class_name | ||
.map(|name| name == "C_PlantedC4") | ||
.unwrap_or(false) | ||
|
@@ -148,29 +194,63 @@ impl BombInfo { | |
} | ||
} | ||
|
||
/// % of the screens height | ||
const PLAYER_AVATAR_TOP_OFFSET: f32 = 0.004; | ||
|
||
/// % of the screens height | ||
const PLAYER_AVATAR_SIZE: f32 = 0.05; | ||
|
||
impl Enhancement for BombInfo { | ||
fn update(&mut self, ctx: &crate::UpdateContext) -> anyhow::Result<()> { | ||
fn update(&mut self, ctx: &UpdateContext) -> anyhow::Result<()> { | ||
if !ctx.settings.bomb_timer { | ||
return Ok(()); | ||
} | ||
|
||
for entity_identity in ctx.cs2_entities.all_identities() { | ||
let entity_class = ctx | ||
.class_name_cache | ||
.lookup(&entity_identity.entity_class_info()?)?; | ||
|
||
if !entity_class | ||
.map(|name| *name == "C_CSPlayerPawn") | ||
.unwrap_or(false) | ||
{ | ||
/* entity is not a player pawn */ | ||
continue; | ||
} | ||
|
||
let player_pawn = entity_identity | ||
.entity_ptr::<C_CSPlayerPawn>()? | ||
.reference_schema() | ||
.context("missing player pawn")?; | ||
|
||
let player_has_defuser = player_pawn | ||
.m_pItemServices()? | ||
.cast::<CCSPlayer_ItemServices>() | ||
.reference_schema()? | ||
.m_bHasDefuser()?; | ||
|
||
if player_pawn.m_iHealth().unwrap_or(0) != 0 && player_has_defuser { | ||
self.any_kit = true; | ||
break; | ||
} | ||
} | ||
|
||
let local_controller = ctx | ||
.cs2_entities | ||
.get_local_player_controller()? | ||
.try_reference_schema() | ||
.with_context(|| obfstr!("failed to read local player controller").to_string())?; | ||
|
||
let local_controller = match local_controller { | ||
Some(controller) => controller, | ||
None => { | ||
/* We're currently not connected */ | ||
return Ok(()); | ||
} | ||
}; | ||
|
||
self.local_team = local_controller.m_iPendingTeamNum()?; | ||
self.bomb_state = self.read_state(ctx)?; | ||
Ok(()) | ||
} | ||
|
||
fn render( | ||
&self, | ||
settings: &crate::settings::AppSettings, | ||
ui: &imgui::Ui, | ||
_view: &crate::view::ViewController, | ||
) { | ||
if !settings.bomb_timer { | ||
fn render(&self, ctx: RenderContext) { | ||
if !ctx.settings.bomb_timer { | ||
return; | ||
} | ||
|
||
|
@@ -179,63 +259,141 @@ impl Enhancement for BombInfo { | |
None => return, | ||
}; | ||
|
||
let group = ui.begin_group(); | ||
let app_visible = ctx.app.settings_visible; | ||
let show_bg_game = ctx.settings.bomb_timer_decor || app_visible; | ||
let show_bg_menu = show_bg_game && app_visible; | ||
|
||
ctx.ui | ||
.window(obfstr!("Bomb Info")) | ||
.size([250.0, 125.0], Condition::Appearing) | ||
// Disable all window decorations. | ||
.resizable(show_bg_menu) | ||
.collapsible(show_bg_menu) | ||
.title_bar(show_bg_menu) | ||
.draw_background(show_bg_game) | ||
.movable(app_visible) | ||
.mouse_inputs(app_visible) | ||
.build(|| { | ||
// Common Colors | ||
let white = [1.0, 1.0, 1.0, 1.0]; // White | ||
|
||
let mut orange = [1.0, 0.61, 0.11, 1.0]; // Orange | ||
let mut green = [0.11, 0.79, 0.26, 1.0]; // Green | ||
let mut red = [0.79, 0.11, 0.11, 1.0]; // Red | ||
|
||
// Reset Colors | ||
if !ctx.settings.bomb_timer_color { | ||
orange = white; | ||
green = white; | ||
red = white; | ||
} | ||
|
||
let line_count = match &bomb_info.state { | ||
C4State::Active { .. } => 3, | ||
C4State::Defused | C4State::Detonated => 2, | ||
}; | ||
let text_height = ui.text_line_height_with_spacing() * line_count as f32; | ||
|
||
/* align to be on the right side after the players */ | ||
let offset_x = ui.io().display_size[0] * 1730.0 / 2560.0; | ||
let offset_y = ui.io().display_size[1] * PLAYER_AVATAR_TOP_OFFSET; | ||
let offset_y = offset_y | ||
+ 0_f32.max((ui.io().display_size[1] * PLAYER_AVATAR_SIZE - text_height) / 2.0); | ||
|
||
ui.set_cursor_pos([offset_x, offset_y]); | ||
ui.text(&format!( | ||
"Bomb planted {}", | ||
if bomb_info.bomb_site == 0 { "A" } else { "B" } | ||
)); | ||
|
||
match &bomb_info.state { | ||
C4State::Active { | ||
time_detonation, | ||
defuse, | ||
} => { | ||
ui.set_cursor_pos_x(offset_x); | ||
ui.text(&format!("Time: {:.3}", time_detonation)); | ||
if let Some(defuse) = defuse.as_ref() { | ||
let color = if defuse.time_remaining > *time_detonation { | ||
[0.79, 0.11, 0.11, 1.0] | ||
// Helper Function | ||
fn helper_text_color( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This function is not needed. You can use:
and instead use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Still pending |
||
ctx: &RenderContext, | ||
player_team: &u8, | ||
color_t: impl Into<Vector4<f32>>, | ||
text_t: &str, | ||
color_ct: impl Into<Vector4<f32>>, | ||
text_ct: &str, | ||
) { | ||
if matches!(&player_team, 2) { | ||
// Terrorists | ||
ctx.ui.text_colored(color_t, text_t) | ||
} else { | ||
[0.11, 0.79, 0.26, 1.0] | ||
}; | ||
|
||
ui.set_cursor_pos_x(offset_x); | ||
ui.text_colored( | ||
color, | ||
&format!( | ||
"Defused in {:.3} by {}", | ||
defuse.time_remaining, defuse.player_name | ||
), | ||
// Counter-Terrorists | ||
ctx.ui.text_colored(color_ct, text_ct) | ||
} | ||
} | ||
|
||
if !matches!(&bomb_info.state, C4State::Dropped) { | ||
RemainingToast marked this conversation as resolved.
Show resolved
Hide resolved
|
||
let plant_str = &format!( | ||
"Bomb planted {}", | ||
if bomb_info.bomb_site == 0 { "A" } else { "B" } | ||
); | ||
} else { | ||
ui.set_cursor_pos_x(offset_x); | ||
ui.text("Not defusing"); | ||
|
||
helper_text_color(&ctx, &self.local_team, green, plant_str, red, plant_str); | ||
} | ||
} | ||
C4State::Defused => { | ||
ui.set_cursor_pos_x(offset_x); | ||
ui.text("Bomb has been defused"); | ||
} | ||
C4State::Detonated => { | ||
ui.set_cursor_pos_x(offset_x); | ||
ui.text("Bomb has been detonated"); | ||
} | ||
} | ||
|
||
group.end(); | ||
match &bomb_info.state { | ||
C4State::Active { | ||
time_detonation, | ||
defuse, | ||
} => { | ||
let ten_seconds = *time_detonation < 10f32; | ||
let five_seconds = *time_detonation < 5f32; | ||
let is_terrorist = matches!(&self.local_team, 2); | ||
|
||
let mut boom_color = white; // white by default | ||
|
||
if *&self.any_kit { | ||
if is_terrorist { | ||
boom_color = red; | ||
if ten_seconds { | ||
boom_color = orange; | ||
} | ||
if five_seconds { | ||
boom_color = green; | ||
} | ||
} else { | ||
boom_color = green; | ||
if ten_seconds { | ||
boom_color = orange; | ||
} | ||
if five_seconds { | ||
boom_color = red; | ||
} | ||
} | ||
} else if ten_seconds { | ||
boom_color = if is_terrorist { green } else { red }; | ||
} | ||
|
||
ctx.ui | ||
.text_colored(boom_color, &format!("Time: {:.3}", time_detonation)); | ||
|
||
if let Some(defuse) = defuse.as_ref() { | ||
let defuse_str = &format!( | ||
"Defused in {:.3} by {}", | ||
defuse.time_remaining, defuse.player_name | ||
); | ||
|
||
if defuse.time_remaining > *time_detonation { | ||
helper_text_color( | ||
&ctx, | ||
&self.local_team, | ||
green, | ||
defuse_str, | ||
red, | ||
defuse_str, | ||
); | ||
} else { | ||
helper_text_color( | ||
&ctx, | ||
&self.local_team, | ||
red, | ||
defuse_str, | ||
green, | ||
defuse_str, | ||
); | ||
}; | ||
} else { | ||
let text = "Not being defused"; | ||
helper_text_color(&ctx, &self.local_team, green, text, red, text); | ||
} | ||
} | ||
C4State::Defused => { | ||
let text = "Bomb has been defused"; | ||
helper_text_color(&ctx, &self.local_team, red, text, green, text); | ||
} | ||
C4State::Detonated => { | ||
let text = "Bomb has been detonated"; | ||
helper_text_color(&ctx, &self.local_team, green, text, red, text); | ||
} | ||
C4State::Dropped => { | ||
let text = "Bomb has been dropped"; | ||
helper_text_color(&ctx, &self.local_team, red, text, orange, text); | ||
} | ||
} | ||
}); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,7 +10,7 @@ pub trait Enhancement { | |
Ok(false) | ||
} | ||
|
||
fn render(&self, settings: &AppSettings, ui: &imgui::Ui, view: &ViewController); | ||
fn render(&self, ctx: RenderContext); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Still: Please pass RenderContext as reference: |
||
fn render_debug_window(&mut self, _settings: &mut AppSettings, _ui: &imgui::Ui) {} | ||
} | ||
|
||
|
@@ -30,6 +30,6 @@ mod aim; | |
pub use aim::*; | ||
|
||
use crate::{ | ||
view::ViewController, | ||
RenderContext, | ||
UpdateContext, | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What does ensure, that
C_CSGameRulesProxy
always comes before a potentialC_PlantedC4
entity? I'm not sure if or under what circumstances but this may lead to unexpected results.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the bomb is dropped it can't be planted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What should I do?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You first have to loop trough all entities in order to find the
C_CSGameRulesProxy
entity.If found & bomb is dropped, then return C4Info with the dropped state.
If not, try to find the bomb entity and get a state from that :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thats what Im already doing tho? @WolverinDEV
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.