mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Merge branch 'imbris/nametags' into 'master'
Make nametages etc fixed size and only display within limited range See merge request veloren/veloren!754
This commit is contained in:
commit
ecf36cb778
@ -81,6 +81,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Significantly reduced the use of warp during world generation.
|
||||
- Parallelized and otherwise sped up significant parts of world generation.
|
||||
- Various performance improvements to world generation.
|
||||
- Nametags now a fixed size and shown in a limited range
|
||||
|
||||
### Removed
|
||||
|
||||
|
@ -23,9 +23,6 @@ void main() {
|
||||
f_color = v_color;
|
||||
|
||||
if (w_pos.w == 1.0) {
|
||||
// In-game element
|
||||
gl_Position = proj_mat * (view_mat * w_pos + vec4(v_pos, 0.0, 0.0));
|
||||
} else if (w_pos.w == -1.0 ) {
|
||||
// Fixed scale In-game element
|
||||
vec4 projected_pos = proj_mat * view_mat * vec4(w_pos.xyz, 1.0);
|
||||
gl_Position = vec4(projected_pos.xy / projected_pos.w + v_pos, 0.0, 1.0);
|
||||
|
@ -18,6 +18,9 @@ pub struct HpFloaterList {
|
||||
// Keep from spawning more floaters from same hp change
|
||||
// Note: this can't detect a change if equivalent healing and damage take place simultaneously
|
||||
pub last_hp: u32,
|
||||
// The time since you last damaged this entity
|
||||
// Used to display nametags outside normal range if this time is below a certain value
|
||||
pub time_since_last_dmg_by_me: Option<f32>,
|
||||
}
|
||||
impl Component for HpFloaterList {
|
||||
type Storage = IDVStorage<Self>;
|
||||
|
@ -43,6 +43,7 @@ impl<'a> System<'a> for Sys {
|
||||
HpFloaterList {
|
||||
floaters: Vec::new(),
|
||||
last_hp,
|
||||
time_since_last_dmg_by_me: None,
|
||||
},
|
||||
);
|
||||
}
|
||||
@ -53,6 +54,12 @@ impl<'a> System<'a> for Sys {
|
||||
.join()
|
||||
.map(|(e, s, fl)| (e, s.health, fl))
|
||||
{
|
||||
// Increment timer for time since last damaged by me
|
||||
hp_floater_list
|
||||
.time_since_last_dmg_by_me
|
||||
.as_mut()
|
||||
.map(|t| *t += dt.0);
|
||||
|
||||
// Check if health has changed (won't work if damaged and then healed with
|
||||
// equivalently in the same frame)
|
||||
if hp_floater_list.last_hp != health.current() {
|
||||
@ -65,7 +72,12 @@ impl<'a> System<'a> for Sys {
|
||||
// health changes could be sent to the client as a list of events)
|
||||
if match health.last_change.1.cause {
|
||||
HealthSource::Attack { by } => {
|
||||
my_entity.0 == entity || my_uid.map_or(false, |&uid| by == uid)
|
||||
let by_me = my_uid.map_or(false, |&uid| by == uid);
|
||||
// If the attack was by me also reset this timer
|
||||
if by_me {
|
||||
hp_floater_list.time_since_last_dmg_by_me = Some(0.0);
|
||||
}
|
||||
my_entity.0 == entity || by_me
|
||||
}
|
||||
HealthSource::Suicide => my_entity.0 == entity,
|
||||
HealthSource::World => my_entity.0 == entity,
|
||||
@ -106,6 +118,7 @@ impl<'a> System<'a> for Sys {
|
||||
HpFloaterList {
|
||||
ref mut floaters,
|
||||
ref last_hp,
|
||||
..
|
||||
},
|
||||
) in (&entities, &mut hp_floater_lists).join()
|
||||
{
|
||||
|
@ -79,6 +79,13 @@ const GROUP_COLOR: Color = Color::Rgba(0.47, 0.84, 1.0, 1.0);
|
||||
const FACTION_COLOR: Color = Color::Rgba(0.24, 1.0, 0.48, 1.0);
|
||||
const KILL_COLOR: Color = Color::Rgba(1.0, 0.17, 0.17, 1.0);
|
||||
|
||||
/// Distance at which nametags are visible
|
||||
const NAMETAG_RANGE: f32 = 40.0;
|
||||
/// Time nametags stay visible after doing damage even if they are out of range in seconds
|
||||
const NAMETAG_DMG_TIME: f32 = 60.0;
|
||||
/// Range damaged triggered nametags can be seen
|
||||
const NAMETAG_DMG_RANGE: f32 = 120.0;
|
||||
|
||||
widget_ids! {
|
||||
struct Ids {
|
||||
// Crosshair
|
||||
@ -545,7 +552,6 @@ impl Hud {
|
||||
let scales = ecs.read_storage::<comp::Scale>();
|
||||
let entities = ecs.entities();
|
||||
let me = client.entity();
|
||||
let view_distance = client.view_distance().unwrap_or(1);
|
||||
let own_level = stats
|
||||
.get(client.entity())
|
||||
.map_or(0, |stats| stats.level.level());
|
||||
@ -632,21 +638,25 @@ impl Hud {
|
||||
&stats,
|
||||
&energy,
|
||||
scales.maybe(),
|
||||
hp_floater_lists.maybe(), // Potentially move this to its own loop
|
||||
&hp_floater_lists,
|
||||
)
|
||||
.join()
|
||||
.filter(|(entity, _, _, stats, _, _, _)| {
|
||||
*entity != me && !stats.is_dead
|
||||
//&& stats.health.current() != stats.health.maximum()
|
||||
})
|
||||
// Don't process health bars outside the vd (visibility further limited by ui backend)
|
||||
.filter(|(_, pos, _, _, _, _, _)| {
|
||||
Vec2::from(pos.0 - player_pos)
|
||||
.map2(TerrainChunk::RECT_SIZE, |d: f32, sz| {
|
||||
d.abs() as f32 / sz as f32
|
||||
// Don't show outside a certain range
|
||||
.filter(|(_, pos, _, _, _, _, hpfl)| {
|
||||
pos.0.distance_squared(player_pos)
|
||||
< (if hpfl
|
||||
.time_since_last_dmg_by_me
|
||||
.map_or(false, |t| t < NAMETAG_DMG_TIME)
|
||||
{
|
||||
NAMETAG_DMG_RANGE
|
||||
} else {
|
||||
NAMETAG_RANGE
|
||||
})
|
||||
.magnitude()
|
||||
< view_distance as f32
|
||||
.powi(2)
|
||||
})
|
||||
.map(|(_, pos, interpolated, stats, energy, scale, f)| {
|
||||
(
|
||||
@ -684,7 +694,6 @@ impl Hud {
|
||||
Rectangle::fill_with([82.0, 8.0], Color::Rgba(0.3, 0.3, 0.3, 0.5))
|
||||
.x_y(0.0, -25.0)
|
||||
.position_ingame(pos + Vec3::new(0.0, 0.0, 1.5 * scale + 1.5))
|
||||
.resolution(100.0)
|
||||
.set(back_id, ui_widgets);
|
||||
|
||||
// % HP Filling
|
||||
@ -699,7 +708,6 @@ impl Hud {
|
||||
HP_COLOR
|
||||
}))
|
||||
.position_ingame(pos + Vec3::new(0.0, 0.0, 1.5 * scale + 1.5))
|
||||
.resolution(100.0)
|
||||
.set(health_bar_id, ui_widgets);
|
||||
// % Mana Filling
|
||||
Rectangle::fill_with(
|
||||
@ -711,7 +719,6 @@ impl Hud {
|
||||
)
|
||||
.x_y(4.5 + (energy_percentage / 100.0 * 36.5) - 36.45, -28.0)
|
||||
.position_ingame(pos + Vec3::new(0.0, 0.0, 1.5 * scale + 1.5))
|
||||
.resolution(100.0)
|
||||
.set(mana_bar_id, ui_widgets);
|
||||
|
||||
// Foreground
|
||||
@ -720,12 +727,12 @@ impl Hud {
|
||||
.x_y(0.0, -25.0)
|
||||
.color(Some(Color::Rgba(1.0, 1.0, 1.0, 0.99)))
|
||||
.position_ingame(pos + Vec3::new(0.0, 0.0, 1.5 * scale + 1.5))
|
||||
.resolution(100.0)
|
||||
.set(front_id, ui_widgets);
|
||||
|
||||
// Enemy SCT
|
||||
if let Some(HpFloaterList { floaters, .. }) = hp_floater_list
|
||||
if let Some(floaters) = Some(hp_floater_list)
|
||||
.filter(|fl| !fl.floaters.is_empty() && global_state.settings.gameplay.sct)
|
||||
.map(|l| &l.floaters)
|
||||
{
|
||||
// Colors
|
||||
const WHITE: Rgb<f32> = Rgb::new(1.0, 0.9, 0.8);
|
||||
@ -790,8 +797,6 @@ impl Hud {
|
||||
.color(Color::Rgba(0.0, 0.0, 0.0, fade))
|
||||
.x_y(0.0, y - 3.0)
|
||||
.position_ingame(pos + Vec3::new(0.0, 0.0, 1.5 * scale as f32 + 1.8))
|
||||
.fixed_scale()
|
||||
.resolution(100.0)
|
||||
.set(sct_bg_id, ui_widgets);
|
||||
Text::new(&format!("{}", hp_damage.abs()))
|
||||
.font_size(font_size)
|
||||
@ -802,8 +807,6 @@ impl Hud {
|
||||
Color::Rgba(0.1, 1.0, 0.1, fade)
|
||||
})
|
||||
.position_ingame(pos + Vec3::new(0.0, 0.0, 1.5 * scale as f32 + 1.8))
|
||||
.fixed_scale()
|
||||
.resolution(100.0)
|
||||
.set(sct_id, ui_widgets);
|
||||
} else {
|
||||
for floater in floaters {
|
||||
@ -846,8 +849,6 @@ impl Hud {
|
||||
.position_ingame(
|
||||
pos + Vec3::new(0.0, 0.0, 1.5 * scale as f32 + 1.8),
|
||||
)
|
||||
.fixed_scale()
|
||||
.resolution(100.0)
|
||||
.set(sct_bg_id, ui_widgets);
|
||||
Text::new(&format!("{}", (floater.hp_change).abs()))
|
||||
.font_size(font_size)
|
||||
@ -860,8 +861,6 @@ impl Hud {
|
||||
.position_ingame(
|
||||
pos + Vec3::new(0.0, 0.0, 1.5 * scale as f32 + 1.8),
|
||||
)
|
||||
.fixed_scale()
|
||||
.resolution(100.0)
|
||||
.set(sct_id, ui_widgets);
|
||||
}
|
||||
}
|
||||
@ -1107,19 +1106,24 @@ impl Hud {
|
||||
&stats,
|
||||
players.maybe(),
|
||||
scales.maybe(),
|
||||
&hp_floater_lists,
|
||||
)
|
||||
.join()
|
||||
.filter(|(entity, _, _, stats, _, _)| *entity != me && !stats.is_dead)
|
||||
// Don't process nametags outside the vd (visibility further limited by ui backend)
|
||||
.filter(|(_, pos, _, _, _, _)| {
|
||||
Vec2::from(pos.0 - player_pos)
|
||||
.map2(TerrainChunk::RECT_SIZE, |d: f32, sz| {
|
||||
d.abs() as f32 / sz as f32
|
||||
.filter(|(entity, _, _, stats, _, _, _)| *entity != me && !stats.is_dead)
|
||||
// Don't show outside a certain range
|
||||
.filter(|(_, pos, _, _, _, _, hpfl)| {
|
||||
pos.0.distance_squared(player_pos)
|
||||
< (if hpfl
|
||||
.time_since_last_dmg_by_me
|
||||
.map_or(false, |t| t < NAMETAG_DMG_TIME)
|
||||
{
|
||||
NAMETAG_DMG_RANGE
|
||||
} else {
|
||||
NAMETAG_RANGE
|
||||
})
|
||||
.magnitude()
|
||||
< view_distance as f32
|
||||
.powi(2)
|
||||
})
|
||||
.map(|(_, pos, interpolated, stats, player, scale)| {
|
||||
.map(|(_, pos, interpolated, stats, player, scale, _)| {
|
||||
// TODO: This is temporary
|
||||
// If the player used the default character name display their name instead
|
||||
let name = if stats.name == "Character Name" {
|
||||
@ -1156,14 +1160,12 @@ impl Hud {
|
||||
.color(Color::Rgba(0.0, 0.0, 0.0, 1.0))
|
||||
.x_y(-1.0, -1.0)
|
||||
.position_ingame(pos + Vec3::new(0.0, 0.0, 1.5 * scale + 1.5))
|
||||
.resolution(100.0)
|
||||
.set(name_bg_id, ui_widgets);
|
||||
Text::new(&name)
|
||||
.font_size(20)
|
||||
.color(Color::Rgba(0.61, 0.61, 0.89, 1.0))
|
||||
.x_y(0.0, 0.0)
|
||||
.position_ingame(pos + Vec3::new(0.0, 0.0, 1.5 * scale + 1.5))
|
||||
.resolution(100.0)
|
||||
.set(name_id, ui_widgets);
|
||||
|
||||
// Level
|
||||
@ -1193,7 +1195,6 @@ impl Hud {
|
||||
})
|
||||
.x_y(-37.0, -24.0)
|
||||
.position_ingame(pos + Vec3::new(0.0, 0.0, 1.5 * scale + 1.5))
|
||||
.resolution(100.0)
|
||||
.set(level_id, ui_widgets);
|
||||
if level_comp > 9 {
|
||||
let skull_ani = ((self.pulse * 0.7/*speed factor*/).cos() * 0.5 + 0.5) * 10.0; //Animation timer
|
||||
@ -1206,7 +1207,6 @@ impl Hud {
|
||||
.x_y(-39.0, -25.0)
|
||||
.color(Some(Color::Rgba(1.0, 1.0, 1.0, 1.0)))
|
||||
.position_ingame(pos + Vec3::new(0.0, 0.0, 1.5 * scale + 1.5))
|
||||
.resolution(100.0)
|
||||
.set(level_skull_id, ui_widgets);
|
||||
}
|
||||
}
|
||||
@ -2036,12 +2036,9 @@ impl Hud {
|
||||
self.ui.focus_widget(maybe_id);
|
||||
}
|
||||
let events = self.update_layout(client, global_state, debug_info, dt);
|
||||
let (view_mat, _, _) = camera.compute_dependents(client);
|
||||
let fov = camera.get_fov();
|
||||
self.ui.maintain(
|
||||
&mut global_state.window.renderer_mut(),
|
||||
Some((view_mat, fov)),
|
||||
);
|
||||
let (v_mat, p_mat, _) = camera.compute_dependents(client);
|
||||
self.ui
|
||||
.maintain(&mut global_state.window.renderer_mut(), Some(p_mat * v_mat));
|
||||
|
||||
// Check if item images need to be reloaded
|
||||
self.item_imgs.reload_if_changed(&mut self.ui);
|
||||
|
@ -14,7 +14,7 @@ pub use scale::{Scale, ScaleMode};
|
||||
pub use widgets::{
|
||||
image_frame::ImageFrame,
|
||||
image_slider::ImageSlider,
|
||||
ingame::{Ingame, IngameAnchor, Ingameable},
|
||||
ingame::{Ingame, Ingameable},
|
||||
radio_list::RadioList,
|
||||
toggle_button::ToggleButton,
|
||||
tooltip::{Tooltip, TooltipManager, Tooltipable},
|
||||
@ -257,7 +257,7 @@ impl Ui {
|
||||
self.ui.widget_input(id)
|
||||
}
|
||||
|
||||
pub fn maintain(&mut self, renderer: &mut Renderer, cam_params: Option<(Mat4<f32>, f32)>) {
|
||||
pub fn maintain(&mut self, renderer: &mut Renderer, view_projection_mat: Option<Mat4<f32>>) {
|
||||
// Maintain tooltip manager
|
||||
self.tooltip_manager
|
||||
.maintain(self.ui.global_input(), self.scale.scale_factor_logical());
|
||||
@ -304,13 +304,12 @@ impl Ui {
|
||||
|
||||
enum Placement {
|
||||
Interface,
|
||||
// Number of primitives left to render ingame and relative scaling/resolution
|
||||
InWorld(usize, Option<(f64, f64)>),
|
||||
// Number of primitives left to render ingame and visibility
|
||||
InWorld(usize, bool),
|
||||
};
|
||||
|
||||
let mut placement = Placement::Interface;
|
||||
// TODO: maybe mutate an ingame scale factor instead of this, depends on if we want them to scale with other ui scaling or not
|
||||
let mut p_scale_factor = self.scale.scale_factor_physical();
|
||||
let p_scale_factor = self.scale.scale_factor_physical();
|
||||
|
||||
// Switches to the `Plain` state and completes the previous `Command` if not already in the
|
||||
// `Plain` state.
|
||||
@ -377,7 +376,6 @@ impl Ui {
|
||||
// No primitives left to place in the world at the current position, go back to drawing the interface
|
||||
Placement::InWorld(0, _) => {
|
||||
placement = Placement::Interface;
|
||||
p_scale_factor = self.scale.scale_factor_physical();
|
||||
// Finish current state
|
||||
self.draw_commands.push(match current_state {
|
||||
State::Plain => DrawCommand::plain(start..mesh.vertices().len()),
|
||||
@ -388,22 +386,23 @@ impl Ui {
|
||||
self.draw_commands.push(DrawCommand::WorldPos(None));
|
||||
}
|
||||
// Primitives still left to draw ingame
|
||||
Placement::InWorld(num_prims, res) => match kind {
|
||||
Placement::InWorld(num_prims, visible) => match kind {
|
||||
// Other types aren't drawn & shouldn't decrement the number of primitives left to draw ingame
|
||||
PrimitiveKind::Other(_) => {}
|
||||
// Decrement the number of primitives left
|
||||
_ => placement = Placement::InWorld(num_prims - 1, res),
|
||||
_ => {
|
||||
placement = Placement::InWorld(num_prims - 1, visible);
|
||||
// Behind the camera
|
||||
if !visible {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
},
|
||||
Placement::Interface => {}
|
||||
}
|
||||
|
||||
// Functions for converting for conrod scalar coords to GL vertex coords (-1.0 to 1.0).
|
||||
let (ui_win_w, ui_win_h) = match placement {
|
||||
Placement::InWorld(_, Some(res)) => res,
|
||||
// Behind the camera or far away
|
||||
Placement::InWorld(_, None) => continue,
|
||||
Placement::Interface => (self.ui.win_w, self.ui.win_h),
|
||||
};
|
||||
let (ui_win_w, ui_win_h) = (self.ui.win_w, self.ui.win_h);
|
||||
let vx = |x: f64| (x / ui_win_w * 2.0) as f32;
|
||||
let vy = |y: f64| (y / ui_win_h * 2.0) as f32;
|
||||
let gl_aabr = |rect: Rect| {
|
||||
@ -615,7 +614,7 @@ impl Ui {
|
||||
PrimitiveKind::Other(container) => {
|
||||
if container.type_id == std::any::TypeId::of::<widgets::ingame::State>() {
|
||||
// Calculate the scale factor to pixels at this 3d point using the camera.
|
||||
if let Some((view_mat, fov)) = cam_params {
|
||||
if let Some(view_projection_mat) = view_projection_mat {
|
||||
// Retrieve world position
|
||||
let parameters = container
|
||||
.state_and_style::<widgets::ingame::State, widgets::ingame::Style>()
|
||||
@ -623,16 +622,21 @@ impl Ui {
|
||||
.state
|
||||
.parameters;
|
||||
|
||||
let pos_in_view = view_mat * Vec4::from_point(parameters.pos);
|
||||
|
||||
let scale_factor = self.ui.win_w as f64
|
||||
/ (-2.0
|
||||
* pos_in_view.z as f64
|
||||
* (0.5 * fov as f64).tan()
|
||||
// TODO: make this have no effect for fixed scale
|
||||
* parameters.res as f64);
|
||||
// Don't process ingame elements behind the camera or very far away
|
||||
placement = if scale_factor > 0.2 {
|
||||
let pos_on_screen = (view_projection_mat
|
||||
* Vec4::from_point(parameters.pos))
|
||||
.homogenized();
|
||||
let visible = if pos_on_screen.z > -1.0 && pos_on_screen.z < 1.0 {
|
||||
let x = pos_on_screen.x;
|
||||
let y = pos_on_screen.y;
|
||||
let (w, h) = parameters.dims.into_tuple();
|
||||
let (half_w, half_h) = (w / ui_win_w as f32, h / ui_win_h as f32);
|
||||
(x - half_w < 1.0 && x + half_w > -1.0)
|
||||
&& (y - half_h < 1.0 && y + half_h > -1.0)
|
||||
} else {
|
||||
false
|
||||
};
|
||||
// Don't process ingame elements outside the frustum
|
||||
placement = if visible {
|
||||
// Finish current state
|
||||
self.draw_commands.push(match current_state {
|
||||
State::Plain => {
|
||||
@ -643,12 +647,9 @@ impl Ui {
|
||||
}
|
||||
});
|
||||
start = mesh.vertices().len();
|
||||
// Push new position command
|
||||
let mut world_pos = Vec4::from_point(parameters.pos);
|
||||
if parameters.fixed_scale {
|
||||
world_pos.w = -1.0
|
||||
};
|
||||
|
||||
// Push new position command
|
||||
let world_pos = Vec4::from_point(parameters.pos);
|
||||
if self.ingame_locals.len() > ingame_local_index {
|
||||
renderer
|
||||
.update_consts(
|
||||
@ -664,28 +665,9 @@ impl Ui {
|
||||
.push(DrawCommand::WorldPos(Some(ingame_local_index)));
|
||||
ingame_local_index += 1;
|
||||
|
||||
p_scale_factor = if parameters.fixed_scale {
|
||||
self.scale.scale_factor_physical()
|
||||
} else {
|
||||
((scale_factor * 10.0).log2().round().powi(2) / 10.0)
|
||||
.min(1.6)
|
||||
.max(0.2)
|
||||
};
|
||||
|
||||
// Scale down ingame elements that are close to the camera
|
||||
let res = if parameters.fixed_scale {
|
||||
(self.ui.win_w, self.ui.win_h)
|
||||
} else if scale_factor > 3.2 {
|
||||
let res = parameters.res * scale_factor as f32 / 3.2;
|
||||
(res as f64, res as f64)
|
||||
} else {
|
||||
let res = parameters.res;
|
||||
(res as f64, res as f64)
|
||||
};
|
||||
|
||||
Placement::InWorld(parameters.num, Some(res))
|
||||
Placement::InWorld(parameters.num, true)
|
||||
} else {
|
||||
Placement::InWorld(parameters.num, None)
|
||||
Placement::InWorld(parameters.num, false)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,4 @@
|
||||
use conrod_core::{
|
||||
builder_methods, position::Dimension, widget, Position, Ui, UiCell, Widget, WidgetCommon,
|
||||
};
|
||||
use conrod_core::{widget, Position, Sizeable, Ui, UiCell, Widget, WidgetCommon};
|
||||
use vek::*;
|
||||
|
||||
#[derive(Clone, WidgetCommon)]
|
||||
@ -8,7 +6,8 @@ pub struct Ingame<W> {
|
||||
#[conrod(common_builder)]
|
||||
common: widget::CommonBuilder,
|
||||
widget: W,
|
||||
parameters: IngameParameters,
|
||||
prim_num: usize,
|
||||
pos: Vec3<f32>,
|
||||
}
|
||||
|
||||
pub trait Ingameable: Widget + Sized {
|
||||
@ -47,19 +46,14 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
// All ingame widgets are now fixed scale
|
||||
#[derive(Copy, Clone, PartialEq)]
|
||||
pub struct IngameParameters {
|
||||
// Number of primitive widgets to position in the game at the specified position
|
||||
// Note this could be more than the number of widgets in the widgets field since widgets can contain widgets
|
||||
pub num: usize,
|
||||
pub pos: Vec3<f32>,
|
||||
// Number of pixels per 1 unit in world coordinates (ie a voxel)
|
||||
// Used for widgets that are rasterized before being sent to the gpu (text & images)
|
||||
// Potentially make this automatic based on distance to camera?
|
||||
pub res: f32,
|
||||
// Whether the widgets should be scaled based on distance to the camera or if they should be a
|
||||
// fixed size (res is ignored in that case)
|
||||
pub fixed_scale: bool,
|
||||
pub dims: Vec2<f32>,
|
||||
}
|
||||
|
||||
pub struct State {
|
||||
@ -73,22 +67,11 @@ impl<W: Ingameable> Ingame<W> {
|
||||
pub fn new(pos: Vec3<f32>, widget: W) -> Self {
|
||||
Self {
|
||||
common: widget::CommonBuilder::default(),
|
||||
parameters: IngameParameters {
|
||||
num: widget.prim_count(),
|
||||
pos,
|
||||
res: 1.0,
|
||||
fixed_scale: false,
|
||||
},
|
||||
prim_num: widget.prim_count(),
|
||||
pos,
|
||||
widget,
|
||||
}
|
||||
}
|
||||
pub fn fixed_scale(mut self) -> Self {
|
||||
self.parameters.fixed_scale = true;
|
||||
self
|
||||
}
|
||||
builder_methods! {
|
||||
pub resolution { parameters.res = f32 }
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Ingameable> Widget for Ingame<W> {
|
||||
@ -99,7 +82,11 @@ impl<W: Ingameable> Widget for Ingame<W> {
|
||||
fn init_state(&self, mut id_gen: widget::id::Generator) -> Self::State {
|
||||
State {
|
||||
id: Some(id_gen.next()),
|
||||
parameters: self.parameters,
|
||||
parameters: IngameParameters {
|
||||
num: self.prim_num,
|
||||
pos: self.pos,
|
||||
dims: Vec2::zero(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@ -110,10 +97,19 @@ impl<W: Ingameable> Widget for Ingame<W> {
|
||||
fn update(self, args: widget::UpdateArgs<Self>) -> Self::Event {
|
||||
let widget::UpdateArgs { state, ui, .. } = args;
|
||||
let Ingame {
|
||||
widget, parameters, ..
|
||||
widget,
|
||||
prim_num,
|
||||
pos,
|
||||
..
|
||||
} = self;
|
||||
|
||||
// Update pos if it has changed
|
||||
let parameters = IngameParameters {
|
||||
num: prim_num,
|
||||
pos,
|
||||
dims: Vec2::<f64>::from(widget.get_wh(ui).unwrap_or([1.0, 1.0])).map(|e| e as f32),
|
||||
};
|
||||
|
||||
// Update parameters if it has changed
|
||||
if state.parameters != parameters {
|
||||
state.update(|s| {
|
||||
s.parameters = parameters;
|
||||
@ -129,75 +125,4 @@ impl<W: Ingameable> Widget for Ingame<W> {
|
||||
fn default_y_position(&self, _: &Ui) -> Position {
|
||||
Position::Absolute(0.0)
|
||||
}
|
||||
fn default_x_dimension(&self, _: &Ui) -> Dimension {
|
||||
Dimension::Absolute(1.0)
|
||||
}
|
||||
fn default_y_dimension(&self, _: &Ui) -> Dimension {
|
||||
Dimension::Absolute(1.0)
|
||||
}
|
||||
}
|
||||
|
||||
// Use this if you have multiple widgets that you want to place at the same spot in-game
|
||||
// but don't want to create a new custom widget to contain them both
|
||||
// Note: widgets must be set immediately after settings this
|
||||
// Note: remove this if it ends up unused
|
||||
#[derive(Clone, WidgetCommon)]
|
||||
pub struct IngameAnchor {
|
||||
#[conrod(common_builder)]
|
||||
common: widget::CommonBuilder,
|
||||
parameters: IngameParameters,
|
||||
}
|
||||
impl IngameAnchor {
|
||||
pub fn new(pos: Vec3<f32>) -> Self {
|
||||
IngameAnchor {
|
||||
common: widget::CommonBuilder::default(),
|
||||
parameters: IngameParameters {
|
||||
num: 0,
|
||||
pos,
|
||||
res: 1.0,
|
||||
fixed_scale: false,
|
||||
},
|
||||
}
|
||||
}
|
||||
pub fn for_widget(mut self, widget: impl Ingameable) -> Self {
|
||||
self.parameters.num += widget.prim_count();
|
||||
self
|
||||
}
|
||||
pub fn for_widgets(mut self, widget: impl Ingameable, n: usize) -> Self {
|
||||
self.parameters.num += n * widget.prim_count();
|
||||
self
|
||||
}
|
||||
pub fn for_prims(mut self, num: usize) -> Self {
|
||||
self.parameters.num += num;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Widget for IngameAnchor {
|
||||
type State = State;
|
||||
type Style = Style;
|
||||
type Event = ();
|
||||
|
||||
fn init_state(&self, _: widget::id::Generator) -> Self::State {
|
||||
State {
|
||||
id: None,
|
||||
parameters: self.parameters,
|
||||
}
|
||||
}
|
||||
|
||||
fn style(&self) -> Self::Style {
|
||||
()
|
||||
}
|
||||
|
||||
fn update(self, args: widget::UpdateArgs<Self>) -> Self::Event {
|
||||
let widget::UpdateArgs { id: _, state, .. } = args;
|
||||
let IngameAnchor { parameters, .. } = self;
|
||||
|
||||
// Update pos if it has changed
|
||||
if state.parameters != parameters {
|
||||
state.update(|s| {
|
||||
s.parameters = parameters;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user