mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Merge branch 'aweinstock/enable-rtsim-airships' into 'master'
Enable RtSim Airships. See merge request veloren/veloren!1973
This commit is contained in:
commit
21b20ea75e
@ -66,6 +66,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- A system to add glow and reflection effects to figures (i.e: characters, armour, weapons, etc.)
|
||||
- Merchants will trade wares with players
|
||||
- Airships that can be mounted and flown, and also walked on (`/airship` admin command)
|
||||
- RtSim airships that fly between towns.
|
||||
|
||||
### Changed
|
||||
|
||||
|
@ -123,6 +123,13 @@ pub enum ServerEvent {
|
||||
drop_item: Option<Item>,
|
||||
rtsim_entity: Option<RtSimEntity>,
|
||||
},
|
||||
CreateShip {
|
||||
pos: comp::Pos,
|
||||
ship: comp::ship::Body,
|
||||
mountable: bool,
|
||||
agent: Option<comp::Agent>,
|
||||
rtsim_entity: Option<RtSimEntity>,
|
||||
},
|
||||
CreateWaypoint(Vec3<f32>),
|
||||
ClientDisconnect(EcsEntity),
|
||||
ChunkRequest(EcsEntity, Vec2<i32>),
|
||||
|
@ -80,7 +80,7 @@ pub struct JoinData<'a> {
|
||||
pub dt: &'a DeltaTime,
|
||||
pub controller: &'a Controller,
|
||||
pub inputs: &'a ControllerInputs,
|
||||
pub health: &'a Health,
|
||||
pub health: Option<&'a Health>,
|
||||
pub energy: &'a Energy,
|
||||
pub inventory: &'a Inventory,
|
||||
pub body: &'a Body,
|
||||
@ -111,7 +111,7 @@ pub struct JoinStruct<'a> {
|
||||
pub energy: RestrictedMut<'a, Energy>,
|
||||
pub inventory: RestrictedMut<'a, Inventory>,
|
||||
pub controller: &'a mut Controller,
|
||||
pub health: &'a Health,
|
||||
pub health: Option<&'a Health>,
|
||||
pub body: &'a Body,
|
||||
pub physics: &'a PhysicsState,
|
||||
pub melee_attack: Option<&'a Melee>,
|
||||
|
@ -140,7 +140,7 @@ impl<'a> System<'a> for Sys {
|
||||
&mut energies.restrict_mut(),
|
||||
&mut inventories.restrict_mut(),
|
||||
&mut controllers,
|
||||
&read_data.healths,
|
||||
read_data.healths.maybe(),
|
||||
&read_data.bodies,
|
||||
&read_data.physics_states,
|
||||
&read_data.stats,
|
||||
@ -149,7 +149,7 @@ impl<'a> System<'a> for Sys {
|
||||
.join()
|
||||
{
|
||||
// Being dead overrides all other states
|
||||
if health.is_dead {
|
||||
if health.map_or(false, |h| h.is_dead) {
|
||||
// Do nothing
|
||||
continue;
|
||||
}
|
||||
@ -248,7 +248,7 @@ impl<'a> System<'a> for Sys {
|
||||
energy,
|
||||
inventory,
|
||||
controller: &mut controller,
|
||||
health: &health,
|
||||
health,
|
||||
body: &body,
|
||||
physics: &physics,
|
||||
melee_attack: read_data.melee_attacks.get(entity),
|
||||
|
@ -1006,17 +1006,19 @@ fn handle_spawn_airship(
|
||||
200.0,
|
||||
)
|
||||
});
|
||||
server
|
||||
let mut builder = server
|
||||
.state
|
||||
.create_ship(pos, comp::ship::Body::DefaultAirship, 1, destination)
|
||||
.with(comp::Scale(comp::ship::AIRSHIP_SCALE))
|
||||
.create_ship(pos, comp::ship::Body::DefaultAirship, true)
|
||||
.with(LightEmitter {
|
||||
col: Rgb::new(1.0, 0.65, 0.2),
|
||||
strength: 2.0,
|
||||
flicker: 1.0,
|
||||
animated: true,
|
||||
})
|
||||
.build();
|
||||
});
|
||||
if let Some(pos) = destination {
|
||||
builder = builder.with(comp::Agent::with_destination(pos))
|
||||
}
|
||||
builder.build();
|
||||
|
||||
server.notify_client(
|
||||
client,
|
||||
|
@ -109,6 +109,25 @@ pub fn handle_create_npc(
|
||||
entity.build();
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn handle_create_ship(
|
||||
server: &mut Server,
|
||||
pos: comp::Pos,
|
||||
ship: comp::ship::Body,
|
||||
mountable: bool,
|
||||
agent: Option<Agent>,
|
||||
rtsim_entity: Option<RtSimEntity>,
|
||||
) {
|
||||
let mut entity = server.state.create_ship(pos, ship, mountable);
|
||||
if let Some(agent) = agent {
|
||||
entity = entity.with(agent);
|
||||
}
|
||||
if let Some(rtsim_entity) = rtsim_entity {
|
||||
entity = entity.with(rtsim_entity);
|
||||
}
|
||||
entity.build();
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn handle_shoot(
|
||||
server: &mut Server,
|
||||
|
@ -2,8 +2,8 @@ use crate::{state_ext::StateExt, Server};
|
||||
use common::event::{EventBus, ServerEvent};
|
||||
use common_base::span;
|
||||
use entity_creation::{
|
||||
handle_beam, handle_create_npc, handle_create_waypoint, handle_initialize_character,
|
||||
handle_loaded_character_data, handle_shockwave, handle_shoot,
|
||||
handle_beam, handle_create_npc, handle_create_ship, handle_create_waypoint,
|
||||
handle_initialize_character, handle_loaded_character_data, handle_shockwave, handle_shoot,
|
||||
};
|
||||
use entity_manipulation::{
|
||||
handle_aura, handle_buff, handle_combo_change, handle_damage, handle_delete, handle_destroy,
|
||||
@ -162,6 +162,13 @@ impl Server {
|
||||
home_chunk,
|
||||
rtsim_entity,
|
||||
),
|
||||
ServerEvent::CreateShip {
|
||||
pos,
|
||||
ship,
|
||||
mountable,
|
||||
agent,
|
||||
rtsim_entity,
|
||||
} => handle_create_ship(self, pos, ship, mountable, agent, rtsim_entity),
|
||||
ServerEvent::CreateWaypoint(pos) => handle_create_waypoint(self, pos),
|
||||
ServerEvent::ClientDisconnect(entity) => {
|
||||
frontend_events.push(handle_client_disconnect(self, entity))
|
||||
|
@ -34,9 +34,7 @@ impl Entity {
|
||||
pub fn get_body(&self) -> comp::Body {
|
||||
match self.rng(PERM_GENUS).gen::<f32>() {
|
||||
// we want 5% airships, 45% birds, 50% humans
|
||||
// TODO: uncomment this to re-enable RtSim airships once physics is interpolated well
|
||||
// in multiplayer.
|
||||
//x if x < 0.05 => comp::Body::Ship(comp::ship::Body::DefaultAirship),
|
||||
x if x < 0.05 => comp::Body::Ship(comp::ship::Body::DefaultAirship),
|
||||
x if x < 0.50 => {
|
||||
let species = *(&comp::bird_medium::ALL_SPECIES)
|
||||
.choose(&mut self.rng(PERM_SPECIES))
|
||||
|
@ -2,8 +2,7 @@
|
||||
|
||||
use super::*;
|
||||
use common::{
|
||||
comp,
|
||||
comp::inventory::loadout_builder::LoadoutBuilder,
|
||||
comp::{self, inventory::loadout_builder::LoadoutBuilder},
|
||||
event::{EventBus, ServerEvent},
|
||||
resources::{DeltaTime, Time},
|
||||
terrain::TerrainGrid,
|
||||
@ -103,35 +102,48 @@ impl<'a> System<'a> for Sys {
|
||||
.map(|e| e as f32)
|
||||
+ Vec3::new(0.5, 0.5, 0.0);
|
||||
let body = entity.get_body();
|
||||
server_emitter.emit(ServerEvent::CreateNpc {
|
||||
pos: comp::Pos(spawn_pos),
|
||||
stats: comp::Stats::new(entity.get_name()),
|
||||
health: comp::Health::new(body, 10),
|
||||
loadout: match body {
|
||||
comp::Body::Humanoid(_) => entity.get_loadout(),
|
||||
_ => LoadoutBuilder::new().build(),
|
||||
let pos = comp::Pos(spawn_pos);
|
||||
let agent = Some(comp::Agent::new(
|
||||
None,
|
||||
matches!(body, comp::Body::Humanoid(_)),
|
||||
None,
|
||||
&body,
|
||||
false,
|
||||
));
|
||||
let rtsim_entity = Some(RtSimEntity(id));
|
||||
let event = match body {
|
||||
comp::Body::Ship(ship) => ServerEvent::CreateShip {
|
||||
pos,
|
||||
ship,
|
||||
mountable: false,
|
||||
agent,
|
||||
rtsim_entity,
|
||||
},
|
||||
poise: comp::Poise::new(body),
|
||||
body,
|
||||
agent: Some(comp::Agent::new(
|
||||
None,
|
||||
matches!(body, comp::Body::Humanoid(_)),
|
||||
None,
|
||||
&body,
|
||||
false,
|
||||
)),
|
||||
alignment: match body {
|
||||
comp::Body::Humanoid(_) => comp::Alignment::Npc,
|
||||
_ => comp::Alignment::Wild,
|
||||
_ => ServerEvent::CreateNpc {
|
||||
pos: comp::Pos(spawn_pos),
|
||||
stats: comp::Stats::new(entity.get_name()),
|
||||
health: comp::Health::new(body, 10),
|
||||
loadout: match body {
|
||||
comp::Body::Humanoid(_) => entity.get_loadout(),
|
||||
_ => LoadoutBuilder::new().build(),
|
||||
},
|
||||
poise: comp::Poise::new(body),
|
||||
body,
|
||||
agent,
|
||||
alignment: match body {
|
||||
comp::Body::Humanoid(_) => comp::Alignment::Npc,
|
||||
_ => comp::Alignment::Wild,
|
||||
},
|
||||
scale: match body {
|
||||
comp::Body::Ship(_) => comp::Scale(comp::ship::AIRSHIP_SCALE),
|
||||
_ => comp::Scale(1.0),
|
||||
},
|
||||
drop_item: None,
|
||||
home_chunk: None,
|
||||
rtsim_entity,
|
||||
},
|
||||
scale: match body {
|
||||
comp::Body::Ship(_) => comp::Scale(comp::ship::AIRSHIP_SCALE),
|
||||
_ => comp::Scale(1.0),
|
||||
},
|
||||
drop_item: None,
|
||||
home_chunk: None,
|
||||
rtsim_entity: Some(RtSimEntity(id)),
|
||||
});
|
||||
};
|
||||
server_emitter.emit(event);
|
||||
}
|
||||
|
||||
// Update rtsim with real entity data
|
||||
|
@ -46,8 +46,7 @@ pub trait StateExt {
|
||||
&mut self,
|
||||
pos: comp::Pos,
|
||||
ship: comp::ship::Body,
|
||||
level: u16,
|
||||
destination: Option<Vec3<f32>>,
|
||||
mountable: bool,
|
||||
) -> EcsEntityBuilder;
|
||||
/// Build a projectile
|
||||
fn create_projectile(
|
||||
@ -231,8 +230,7 @@ impl StateExt for State {
|
||||
&mut self,
|
||||
pos: comp::Pos,
|
||||
ship: comp::ship::Body,
|
||||
level: u16,
|
||||
destination: Option<Vec3<f32>>,
|
||||
mountable: bool,
|
||||
) -> EcsEntityBuilder {
|
||||
let mut builder = self
|
||||
.ecs_mut()
|
||||
@ -246,19 +244,18 @@ impl StateExt for State {
|
||||
})
|
||||
.with(comp::Body::Ship(ship))
|
||||
.with(comp::Gravity(1.0))
|
||||
.with(comp::Scale(comp::ship::AIRSHIP_SCALE))
|
||||
.with(comp::Controller::default())
|
||||
.with(comp::inventory::Inventory::new_empty())
|
||||
.with(comp::CharacterState::default())
|
||||
// TODO: some of these are required in order for the character_behavior system to
|
||||
// recognize a possesed airship; that system should be refactored to use `.maybe()`
|
||||
.with(comp::Energy::new(ship.into(), level))
|
||||
.with(comp::Health::new(ship.into(), level))
|
||||
.with(comp::Energy::new(ship.into(), 0))
|
||||
.with(comp::Stats::new("Airship".to_string()))
|
||||
.with(comp::Buffs::default())
|
||||
.with(comp::MountState::Unmounted)
|
||||
.with(comp::Combo::default());
|
||||
if let Some(pos) = destination {
|
||||
builder = builder.with(comp::Agent::with_destination(pos))
|
||||
|
||||
if mountable {
|
||||
builder = builder.with(comp::MountState::Unmounted);
|
||||
}
|
||||
builder
|
||||
}
|
||||
|
@ -130,7 +130,7 @@ impl<'a> System<'a> for Sys {
|
||||
job.cpu_stats.measure(ParMode::Rayon);
|
||||
(
|
||||
&read_data.entities,
|
||||
(&read_data.energies, &read_data.healths),
|
||||
(&read_data.energies, read_data.healths.maybe()),
|
||||
&read_data.positions,
|
||||
&read_data.velocities,
|
||||
&read_data.orientations,
|
||||
@ -241,7 +241,7 @@ impl<'a> System<'a> for Sys {
|
||||
let flees = alignment
|
||||
.map(|a| !matches!(a, Alignment::Enemy | Alignment::Owned(_)))
|
||||
.unwrap_or(true);
|
||||
let damage = health.current() as f32 / health.maximum() as f32;
|
||||
let damage = health.map_or(1.0, |h| h.current() as f32 / h.maximum() as f32);
|
||||
let rtsim_entity = read_data
|
||||
.rtsim_entities
|
||||
.get(entity)
|
||||
@ -394,21 +394,62 @@ impl<'a> System<'a> for Sys {
|
||||
data.idle_tree(agent, controller, &read_data, &mut event_emitter);
|
||||
}
|
||||
} else {
|
||||
// Target an entity that's attacking us if the attack was recent
|
||||
if health.last_change.0 < DAMAGE_MEMORY_DURATION {
|
||||
if let comp::HealthSource::Damage { by: Some(by), .. } =
|
||||
health.last_change.1.cause
|
||||
{
|
||||
if let Some(attacker) =
|
||||
read_data.uid_allocator.retrieve_entity_internal(by.id())
|
||||
// Target an entity that's attacking us if the attack was recent and we
|
||||
// have a health component
|
||||
match health {
|
||||
Some(health) if health.last_change.0 < DAMAGE_MEMORY_DURATION => {
|
||||
if let comp::HealthSource::Damage { by: Some(by), .. } =
|
||||
health.last_change.1.cause
|
||||
{
|
||||
if let Some(tgt_pos) = read_data.positions.get(attacker) {
|
||||
// If the target is dead or in a safezone, remove the target
|
||||
// and idle.
|
||||
if should_stop_attacking(
|
||||
read_data.healths.get(attacker),
|
||||
read_data.buffs.get(attacker),
|
||||
) {
|
||||
if let Some(attacker) =
|
||||
read_data.uid_allocator.retrieve_entity_internal(by.id())
|
||||
{
|
||||
if let Some(tgt_pos) = read_data.positions.get(attacker) {
|
||||
// If the target is dead or in a safezone, remove the
|
||||
// target
|
||||
// and idle.
|
||||
if should_stop_attacking(
|
||||
read_data.healths.get(attacker),
|
||||
read_data.buffs.get(attacker),
|
||||
) {
|
||||
agent.target = None;
|
||||
data.idle_tree(
|
||||
agent,
|
||||
controller,
|
||||
&read_data,
|
||||
&mut event_emitter,
|
||||
);
|
||||
} else {
|
||||
agent.target = Some(Target {
|
||||
target: attacker,
|
||||
hostile: true,
|
||||
});
|
||||
data.attack(
|
||||
agent,
|
||||
controller,
|
||||
&read_data.terrain,
|
||||
tgt_pos,
|
||||
read_data.bodies.get(attacker),
|
||||
&read_data.dt,
|
||||
);
|
||||
// Remember this encounter if an RtSim entity
|
||||
if let Some(tgt_stats) =
|
||||
read_data.stats.get(attacker)
|
||||
{
|
||||
if data.rtsim_entity.is_some() {
|
||||
agent.rtsim_controller.events.push(
|
||||
RtSimEvent::AddMemory(Memory {
|
||||
item: MemoryItem::CharacterFight {
|
||||
name: tgt_stats.name.clone(),
|
||||
},
|
||||
time_to_forget: read_data.time.0
|
||||
+ 300.0,
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
agent.target = None;
|
||||
data.idle_tree(
|
||||
agent,
|
||||
@ -416,50 +457,21 @@ impl<'a> System<'a> for Sys {
|
||||
&read_data,
|
||||
&mut event_emitter,
|
||||
);
|
||||
} else {
|
||||
agent.target = Some(Target {
|
||||
target: attacker,
|
||||
hostile: true,
|
||||
});
|
||||
data.attack(
|
||||
agent,
|
||||
controller,
|
||||
&read_data.terrain,
|
||||
tgt_pos,
|
||||
read_data.bodies.get(attacker),
|
||||
&read_data.dt,
|
||||
);
|
||||
// Remember this encounter if an RtSim entity
|
||||
if let Some(tgt_stats) = read_data.stats.get(attacker) {
|
||||
if data.rtsim_entity.is_some() {
|
||||
agent.rtsim_controller.events.push(
|
||||
RtSimEvent::AddMemory(Memory {
|
||||
item: MemoryItem::CharacterFight {
|
||||
name: tgt_stats.name.clone(),
|
||||
},
|
||||
time_to_forget: read_data.time.0
|
||||
+ 300.0,
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
agent.target = None;
|
||||
data.idle_tree(
|
||||
agent,
|
||||
controller,
|
||||
&read_data,
|
||||
&mut event_emitter,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
agent.target = None;
|
||||
data.idle_tree(
|
||||
agent,
|
||||
controller,
|
||||
&read_data,
|
||||
&mut event_emitter,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
agent.target = None;
|
||||
},
|
||||
_ => {
|
||||
data.idle_tree(agent, controller, &read_data, &mut event_emitter);
|
||||
}
|
||||
} else {
|
||||
data.idle_tree(agent, controller, &read_data, &mut event_emitter);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1339,7 +1339,7 @@ impl Hud {
|
||||
&pos,
|
||||
interpolated.maybe(),
|
||||
&stats,
|
||||
&healths,
|
||||
healths.maybe(),
|
||||
&buffs,
|
||||
energy.maybe(),
|
||||
scales.maybe(),
|
||||
@ -1352,7 +1352,7 @@ impl Hud {
|
||||
.filter(|t| {
|
||||
let health = t.4;
|
||||
let entity = t.0;
|
||||
entity != me && !health.is_dead
|
||||
entity != me && !health.map_or(false, |h| h.is_dead)
|
||||
})
|
||||
.filter_map(
|
||||
|(
|
||||
@ -1380,7 +1380,7 @@ impl Hud {
|
||||
let display_overhead_info =
|
||||
(info.target_entity.map_or(false, |e| e == entity)
|
||||
|| info.selected_entity.map_or(false, |s| s.0 == entity)
|
||||
|| overhead::should_show_healthbar(health)
|
||||
|| health.map_or(true, overhead::should_show_healthbar)
|
||||
|| in_group)
|
||||
&& dist_sqr
|
||||
< (if in_group {
|
||||
@ -1400,9 +1400,9 @@ impl Hud {
|
||||
health,
|
||||
buffs,
|
||||
energy,
|
||||
combat_rating: combat::combat_rating(
|
||||
inventory, health, stats, *body, &msm,
|
||||
),
|
||||
combat_rating: health.map_or(0.0, |health| {
|
||||
combat::combat_rating(inventory, health, stats, *body, &msm)
|
||||
}),
|
||||
});
|
||||
let bubble = if dist_sqr < SPEECH_BUBBLE_RANGE.powi(2) {
|
||||
speech_bubbles.get(uid)
|
||||
@ -1492,7 +1492,8 @@ impl Hud {
|
||||
});
|
||||
// Divide by 10 to stay in the same dimension as the HP display
|
||||
let hp_dmg_rounded_abs = ((hp_damage + 5) / 10).abs();
|
||||
let max_hp_frac = hp_damage.abs() as f32 / health.maximum() as f32;
|
||||
let max_hp_frac =
|
||||
hp_damage.abs() as f32 / health.map_or(1.0, |h| h.maximum() as f32);
|
||||
let timer = floaters
|
||||
.last()
|
||||
.expect("There must be at least one floater")
|
||||
@ -1563,8 +1564,8 @@ impl Hud {
|
||||
let sct_bg_id = sct_bg_walker
|
||||
.next(&mut self.ids.sct_bgs, &mut ui_widgets.widget_id_generator());
|
||||
// Calculate total change
|
||||
let max_hp_frac =
|
||||
floater.hp_change.abs() as f32 / health.maximum() as f32;
|
||||
let max_hp_frac = floater.hp_change.abs() as f32
|
||||
/ health.map_or(1.0, |h| h.maximum() as f32);
|
||||
// Increase font size based on fraction of maximum health
|
||||
// "flashes" by having a larger size in the first 100ms
|
||||
let font_size = 30
|
||||
|
@ -57,7 +57,7 @@ widget_ids! {
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct Info<'a> {
|
||||
pub name: &'a str,
|
||||
pub health: &'a Health,
|
||||
pub health: Option<&'a Health>,
|
||||
pub buffs: &'a Buffs,
|
||||
pub energy: Option<&'a Energy>,
|
||||
pub combat_rating: f32,
|
||||
@ -140,7 +140,7 @@ impl<'a> Ingameable for Overhead<'a> {
|
||||
} else {
|
||||
0
|
||||
}
|
||||
+ if should_show_healthbar(info.health) {
|
||||
+ if info.health.map_or(false, |h| should_show_healthbar(h)) {
|
||||
5 + if info.energy.is_some() { 1 } else { 0 }
|
||||
} else {
|
||||
0
|
||||
@ -176,10 +176,11 @@ impl<'a> Widget for Overhead<'a> {
|
||||
}) = self.info
|
||||
{
|
||||
// Used to set healthbar colours based on hp_percentage
|
||||
let hp_percentage = health.current() as f64 / health.maximum() as f64 * 100.0;
|
||||
let hp_percentage =
|
||||
health.map_or(100.0, |h| h.current() as f64 / h.maximum() as f64 * 100.0);
|
||||
// Compare levels to decide if a skull is shown
|
||||
let health_current = (health.current() / 10) as f64;
|
||||
let health_max = (health.maximum() / 10) as f64;
|
||||
let health_current = health.map_or(1.0, |h| (h.current() / 10) as f64);
|
||||
let health_max = health.map_or(1.0, |h| (h.maximum() / 10) as f64);
|
||||
let name_y = if (health_current - health_max).abs() < 1e-6 {
|
||||
MANA_BAR_Y + 20.0
|
||||
} else {
|
||||
@ -296,116 +297,120 @@ impl<'a> Widget for Overhead<'a> {
|
||||
.parent(id)
|
||||
.set(state.ids.name, ui);
|
||||
|
||||
if should_show_healthbar(health) {
|
||||
// Show HP Bar
|
||||
let hp_ani = (self.pulse * 4.0/* speed factor */).cos() * 0.5 + 1.0; //Animation timer
|
||||
let crit_hp_color: Color = Color::Rgba(0.93, 0.59, 0.03, hp_ani);
|
||||
match health {
|
||||
Some(health) if should_show_healthbar(health) => {
|
||||
// Show HP Bar
|
||||
let hp_ani = (self.pulse * 4.0/* speed factor */).cos() * 0.5 + 1.0; //Animation timer
|
||||
let crit_hp_color: Color = Color::Rgba(0.93, 0.59, 0.03, hp_ani);
|
||||
|
||||
// Background
|
||||
Image::new(if self.in_group {self.imgs.health_bar_group_bg} else {self.imgs.enemy_health_bg})
|
||||
// Background
|
||||
Image::new(if self.in_group {self.imgs.health_bar_group_bg} else {self.imgs.enemy_health_bg})
|
||||
.w_h(84.0 * BARSIZE, 10.0 * BARSIZE)
|
||||
.x_y(0.0, MANA_BAR_Y + 6.5) //-25.5)
|
||||
.color(Some(Color::Rgba(0.1, 0.1, 0.1, 0.8)))
|
||||
.parent(id)
|
||||
.set(state.ids.health_bar_bg, ui);
|
||||
|
||||
// % HP Filling
|
||||
let size_factor = (hp_percentage / 100.0) * BARSIZE;
|
||||
let w = if self.in_group {
|
||||
82.0 * size_factor
|
||||
} else {
|
||||
73.0 * size_factor
|
||||
};
|
||||
let h = 6.0 * BARSIZE;
|
||||
let x = if self.in_group {
|
||||
(0.0 + (hp_percentage / 100.0 * 41.0 - 41.0)) * BARSIZE
|
||||
} else {
|
||||
(4.5 + (hp_percentage / 100.0 * 36.45 - 36.45)) * BARSIZE
|
||||
};
|
||||
Image::new(self.imgs.enemy_bar)
|
||||
.w_h(w, h)
|
||||
.x_y(x, MANA_BAR_Y + 8.0)
|
||||
.color(if self.in_group {
|
||||
// Different HP bar colors only for group members
|
||||
Some(match hp_percentage {
|
||||
x if (0.0..25.0).contains(&x) => crit_hp_color,
|
||||
x if (25.0..50.0).contains(&x) => LOW_HP_COLOR,
|
||||
_ => HP_COLOR,
|
||||
})
|
||||
} else {
|
||||
Some(ENEMY_HP_COLOR)
|
||||
})
|
||||
.parent(id)
|
||||
.set(state.ids.health_bar, ui);
|
||||
let mut txt = format!("{}/{}", health_cur_txt, health_max_txt);
|
||||
if health.is_dead {
|
||||
txt = self.i18n.get("hud.group.dead").to_string()
|
||||
};
|
||||
Text::new(&txt)
|
||||
.mid_top_with_margin_on(state.ids.health_bar_bg, 2.0)
|
||||
.font_size(10)
|
||||
.font_id(self.fonts.cyri.conrod_id)
|
||||
.color(TEXT_COLOR)
|
||||
.parent(id)
|
||||
.set(state.ids.health_txt, ui);
|
||||
|
||||
// % Mana Filling
|
||||
if let Some(energy) = energy {
|
||||
let energy_factor = energy.current() as f64 / energy.maximum() as f64;
|
||||
let size_factor = energy_factor * BARSIZE;
|
||||
// % HP Filling
|
||||
let size_factor = (hp_percentage / 100.0) * BARSIZE;
|
||||
let w = if self.in_group {
|
||||
80.0 * size_factor
|
||||
82.0 * size_factor
|
||||
} else {
|
||||
72.0 * size_factor
|
||||
73.0 * size_factor
|
||||
};
|
||||
let h = 6.0 * BARSIZE;
|
||||
let x = if self.in_group {
|
||||
((0.0 + (energy_factor * 40.0)) - 40.0) * BARSIZE
|
||||
(0.0 + (hp_percentage / 100.0 * 41.0 - 41.0)) * BARSIZE
|
||||
} else {
|
||||
((3.5 + (energy_factor * 36.5)) - 36.45) * BARSIZE
|
||||
(4.5 + (hp_percentage / 100.0 * 36.45 - 36.45)) * BARSIZE
|
||||
};
|
||||
Rectangle::fill_with([w, MANA_BAR_HEIGHT], STAMINA_COLOR)
|
||||
.x_y(
|
||||
x, MANA_BAR_Y, //-32.0,
|
||||
)
|
||||
Image::new(self.imgs.enemy_bar)
|
||||
.w_h(w, h)
|
||||
.x_y(x, MANA_BAR_Y + 8.0)
|
||||
.color(if self.in_group {
|
||||
// Different HP bar colors only for group members
|
||||
Some(match hp_percentage {
|
||||
x if (0.0..25.0).contains(&x) => crit_hp_color,
|
||||
x if (25.0..50.0).contains(&x) => LOW_HP_COLOR,
|
||||
_ => HP_COLOR,
|
||||
})
|
||||
} else {
|
||||
Some(ENEMY_HP_COLOR)
|
||||
})
|
||||
.parent(id)
|
||||
.set(state.ids.mana_bar, ui);
|
||||
}
|
||||
.set(state.ids.health_bar, ui);
|
||||
let mut txt = format!("{}/{}", health_cur_txt, health_max_txt);
|
||||
if health.is_dead {
|
||||
txt = self.i18n.get("hud.group.dead").to_string()
|
||||
};
|
||||
Text::new(&txt)
|
||||
.mid_top_with_margin_on(state.ids.health_bar_bg, 2.0)
|
||||
.font_size(10)
|
||||
.font_id(self.fonts.cyri.conrod_id)
|
||||
.color(TEXT_COLOR)
|
||||
.parent(id)
|
||||
.set(state.ids.health_txt, ui);
|
||||
|
||||
// Foreground
|
||||
Image::new(if self.in_group {self.imgs.health_bar_group} else {self.imgs.enemy_health})
|
||||
// % Mana Filling
|
||||
if let Some(energy) = energy {
|
||||
let energy_factor = energy.current() as f64 / energy.maximum() as f64;
|
||||
let size_factor = energy_factor * BARSIZE;
|
||||
let w = if self.in_group {
|
||||
80.0 * size_factor
|
||||
} else {
|
||||
72.0 * size_factor
|
||||
};
|
||||
let x = if self.in_group {
|
||||
((0.0 + (energy_factor * 40.0)) - 40.0) * BARSIZE
|
||||
} else {
|
||||
((3.5 + (energy_factor * 36.5)) - 36.45) * BARSIZE
|
||||
};
|
||||
Rectangle::fill_with([w, MANA_BAR_HEIGHT], STAMINA_COLOR)
|
||||
.x_y(
|
||||
x, MANA_BAR_Y, //-32.0,
|
||||
)
|
||||
.parent(id)
|
||||
.set(state.ids.mana_bar, ui);
|
||||
}
|
||||
|
||||
// Foreground
|
||||
Image::new(if self.in_group {self.imgs.health_bar_group} else {self.imgs.enemy_health})
|
||||
.w_h(84.0 * BARSIZE, 10.0 * BARSIZE)
|
||||
.x_y(0.0, MANA_BAR_Y + 6.5) //-25.5)
|
||||
.color(Some(Color::Rgba(1.0, 1.0, 1.0, 0.99)))
|
||||
.parent(id)
|
||||
.set(state.ids.health_bar_fg, ui);
|
||||
|
||||
let indicator_col = cr_color(combat_rating);
|
||||
let artifact_diffculty = 122.0;
|
||||
let indicator_col = cr_color(combat_rating);
|
||||
let artifact_diffculty = 122.0;
|
||||
|
||||
if combat_rating > artifact_diffculty && !self.in_group {
|
||||
let skull_ani = ((self.pulse * 0.7/* speed factor */).cos() * 0.5 + 0.5) * 10.0; //Animation timer
|
||||
Image::new(if skull_ani as i32 == 1 && rand::random::<f32>() < 0.9 {
|
||||
self.imgs.skull_2
|
||||
if combat_rating > artifact_diffculty && !self.in_group {
|
||||
let skull_ani =
|
||||
((self.pulse * 0.7/* speed factor */).cos() * 0.5 + 0.5) * 10.0; //Animation timer
|
||||
Image::new(if skull_ani as i32 == 1 && rand::random::<f32>() < 0.9 {
|
||||
self.imgs.skull_2
|
||||
} else {
|
||||
self.imgs.skull
|
||||
})
|
||||
.w_h(18.0 * BARSIZE, 18.0 * BARSIZE)
|
||||
.x_y(-39.0 * BARSIZE, MANA_BAR_Y + 7.0)
|
||||
.color(Some(Color::Rgba(1.0, 1.0, 1.0, 1.0)))
|
||||
.parent(id)
|
||||
.set(state.ids.level_skull, ui);
|
||||
} else {
|
||||
self.imgs.skull
|
||||
})
|
||||
.w_h(18.0 * BARSIZE, 18.0 * BARSIZE)
|
||||
.x_y(-39.0 * BARSIZE, MANA_BAR_Y + 7.0)
|
||||
.color(Some(Color::Rgba(1.0, 1.0, 1.0, 1.0)))
|
||||
.parent(id)
|
||||
.set(state.ids.level_skull, ui);
|
||||
} else {
|
||||
Image::new(if self.in_group {
|
||||
self.imgs.nothing
|
||||
} else {
|
||||
self.imgs.combat_rating_ico
|
||||
})
|
||||
.w_h(7.0 * BARSIZE, 7.0 * BARSIZE)
|
||||
.x_y(-37.0 * BARSIZE, MANA_BAR_Y + 6.0)
|
||||
.color(Some(indicator_col))
|
||||
.parent(id)
|
||||
.set(state.ids.level, ui);
|
||||
}
|
||||
Image::new(if self.in_group {
|
||||
self.imgs.nothing
|
||||
} else {
|
||||
self.imgs.combat_rating_ico
|
||||
})
|
||||
.w_h(7.0 * BARSIZE, 7.0 * BARSIZE)
|
||||
.x_y(-37.0 * BARSIZE, MANA_BAR_Y + 6.0)
|
||||
.color(Some(indicator_col))
|
||||
.parent(id)
|
||||
.set(state.ids.level, ui);
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
// Speech bubble
|
||||
|
Loading…
Reference in New Issue
Block a user