character viewpoint

This commit is contained in:
IsseW 2022-07-06 11:20:18 +02:00
parent bf44ebd12b
commit a165bc09bc
18 changed files with 398 additions and 287 deletions

View File

@ -64,4 +64,5 @@ gameinput-mapzoomin = Increase map zoom
gameinput-mapzoomout = Decrease map zoom gameinput-mapzoomout = Decrease map zoom
gameinput-greet = Greet gameinput-greet = Greet
gameinput-map-locationmarkerbutton = Set a waypoint in the Map gameinput-map-locationmarkerbutton = Set a waypoint in the Map
gameinput-spectatespeedboost = Spectate speed boost gameinput-spectatespeedboost = Spectate speed boost
gameinput-spectateviewpoint = Spectate viewpoint

View File

@ -150,6 +150,8 @@ pub enum GameInput {
MapSetMarker, MapSetMarker,
#[strum(serialize = "gameinput.spectatespeedboost")] #[strum(serialize = "gameinput.spectatespeedboost")]
SpectateSpeedBoost, SpectateSpeedBoost,
#[strum(serialize = "gameinput.spectateviewpoint")]
SpectateViewpoint,
} }
impl GameInput { impl GameInput {

View File

@ -3,7 +3,7 @@ use super::{
img_ids::{Imgs, ImgsRot}, img_ids::{Imgs, ImgsRot},
item_imgs::ItemImgs, item_imgs::ItemImgs,
slots::{ArmorSlot, EquipSlot, InventorySlot, SlotManager}, slots::{ArmorSlot, EquipSlot, InventorySlot, SlotManager},
Show, CRITICAL_HP_COLOR, LOW_HP_COLOR, TEXT_COLOR, UI_HIGHLIGHT_0, UI_MAIN, HudInfo, Show, CRITICAL_HP_COLOR, LOW_HP_COLOR, TEXT_COLOR, UI_HIGHLIGHT_0, UI_MAIN,
}; };
use crate::{ use crate::{
game_input::GameInput, game_input::GameInput,
@ -563,6 +563,7 @@ widget_ids! {
#[derive(WidgetCommon)] #[derive(WidgetCommon)]
pub struct Bag<'a> { pub struct Bag<'a> {
client: &'a Client, client: &'a Client,
info: &'a HudInfo,
global_state: &'a GlobalState, global_state: &'a GlobalState,
imgs: &'a Imgs, imgs: &'a Imgs,
item_imgs: &'a ItemImgs, item_imgs: &'a ItemImgs,
@ -589,6 +590,7 @@ impl<'a> Bag<'a> {
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
pub fn new( pub fn new(
client: &'a Client, client: &'a Client,
info: &'a HudInfo,
global_state: &'a GlobalState, global_state: &'a GlobalState,
imgs: &'a Imgs, imgs: &'a Imgs,
item_imgs: &'a ItemImgs, item_imgs: &'a ItemImgs,
@ -610,6 +612,7 @@ impl<'a> Bag<'a> {
) -> Self { ) -> Self {
Self { Self {
client, client,
info,
global_state, global_state,
imgs, imgs,
item_imgs, item_imgs,
@ -695,7 +698,7 @@ impl<'a> Widget for Bag<'a> {
.font_id(self.fonts.cyri.conrod_id) .font_id(self.fonts.cyri.conrod_id)
.desc_text_color(TEXT_COLOR); .desc_text_color(TEXT_COLOR);
let inventories = self.client.inventories(); let inventories = self.client.inventories();
let inventory = match inventories.get(self.client.entity()) { let inventory = match inventories.get(self.info.viewpoint_entity) {
Some(l) => l, Some(l) => l,
None => return None, None => return None,
}; };
@ -733,6 +736,7 @@ impl<'a> Widget for Bag<'a> {
) )
}, },
self.client, self.client,
self.info,
self.imgs, self.imgs,
self.item_imgs, self.item_imgs,
self.pulse, self.pulse,
@ -759,7 +763,7 @@ impl<'a> Widget for Bag<'a> {
true, true,
&item_tooltip, &item_tooltip,
self.stats.name.to_string(), self.stats.name.to_string(),
self.client.entity(), self.info.viewpoint_entity,
true, true,
inventory, inventory,
&state.bg_ids, &state.bg_ids,

View File

@ -1,6 +1,6 @@
use super::{ use super::{
img_ids::{Imgs, ImgsRot}, img_ids::{Imgs, ImgsRot},
BLACK, CRITICAL_HP_COLOR, LOW_HP_COLOR, QUALITY_LEGENDARY, TEXT_COLOR, HudInfo, BLACK, CRITICAL_HP_COLOR, LOW_HP_COLOR, QUALITY_LEGENDARY, TEXT_COLOR,
}; };
use crate::{ use crate::{
game_input::GameInput, game_input::GameInput,
@ -51,6 +51,7 @@ widget_ids! {
#[derive(WidgetCommon)] #[derive(WidgetCommon)]
pub struct Buttons<'a> { pub struct Buttons<'a> {
client: &'a Client, client: &'a Client,
info: &'a HudInfo,
show_bag: bool, show_bag: bool,
imgs: &'a Imgs, imgs: &'a Imgs,
fonts: &'a Fonts, fonts: &'a Fonts,
@ -68,6 +69,7 @@ pub struct Buttons<'a> {
impl<'a> Buttons<'a> { impl<'a> Buttons<'a> {
pub fn new( pub fn new(
client: &'a Client, client: &'a Client,
info: &'a HudInfo,
show_bag: bool, show_bag: bool,
imgs: &'a Imgs, imgs: &'a Imgs,
fonts: &'a Fonts, fonts: &'a Fonts,
@ -81,6 +83,7 @@ impl<'a> Buttons<'a> {
) -> Self { ) -> Self {
Self { Self {
client, client,
info,
show_bag, show_bag,
imgs, imgs,
fonts, fonts,
@ -192,7 +195,7 @@ impl<'a> Widget for Buttons<'a> {
); );
} }
let invs = self.client.inventories(); let invs = self.client.inventories();
let inventory = match invs.get(self.client.entity()) { let inventory = match invs.get(self.info.viewpoint_entity) {
Some(inv) => inv, Some(inv) => inv,
None => return None, None => return None,
}; };

View File

@ -3,7 +3,7 @@ use super::{
img_ids::{Imgs, ImgsRot}, img_ids::{Imgs, ImgsRot},
item_imgs::{animate_by_pulse, ItemImgs}, item_imgs::{animate_by_pulse, ItemImgs},
slots::{CraftSlot, CraftSlotInfo, SlotManager}, slots::{CraftSlot, CraftSlotInfo, SlotManager},
Show, TEXT_COLOR, TEXT_DULL_RED_COLOR, TEXT_GRAY_COLOR, UI_HIGHLIGHT_0, UI_MAIN, HudInfo, Show, TEXT_COLOR, TEXT_DULL_RED_COLOR, TEXT_GRAY_COLOR, UI_HIGHLIGHT_0, UI_MAIN,
}; };
use crate::ui::{ use crate::ui::{
fonts::Fonts, fonts::Fonts,
@ -137,6 +137,7 @@ impl Default for CraftingShow {
#[derive(WidgetCommon)] #[derive(WidgetCommon)]
pub struct Crafting<'a> { pub struct Crafting<'a> {
client: &'a Client, client: &'a Client,
info: &'a HudInfo,
imgs: &'a Imgs, imgs: &'a Imgs,
fonts: &'a Fonts, fonts: &'a Fonts,
localized_strings: &'a Localization, localized_strings: &'a Localization,
@ -156,6 +157,7 @@ pub struct Crafting<'a> {
impl<'a> Crafting<'a> { impl<'a> Crafting<'a> {
pub fn new( pub fn new(
client: &'a Client, client: &'a Client,
info: &'a HudInfo,
imgs: &'a Imgs, imgs: &'a Imgs,
fonts: &'a Fonts, fonts: &'a Fonts,
localized_strings: &'a Localization, localized_strings: &'a Localization,
@ -171,6 +173,7 @@ impl<'a> Crafting<'a> {
) -> Self { ) -> Self {
Self { Self {
client, client,
info,
imgs, imgs,
fonts, fonts,
localized_strings, localized_strings,
@ -321,6 +324,7 @@ impl<'a> Widget for Crafting<'a> {
) )
}, },
self.client, self.client,
self.info,
self.imgs, self.imgs,
self.item_imgs, self.item_imgs,
self.pulse, self.pulse,

View File

@ -4,6 +4,8 @@ use common::comp::{
}; };
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use super::HudInfo;
#[derive(Clone, Copy, Debug, PartialEq)] #[derive(Clone, Copy, Debug, PartialEq)]
pub enum Slot { pub enum Slot {
One = 0, One = 0,
@ -63,22 +65,22 @@ impl State {
// TODO: remove pending UI // TODO: remove pending UI
// Adds ability slots if missing and should be present // Adds ability slots if missing and should be present
// Removes ability slots if not there and shouldn't be present // Removes ability slots if not there and shouldn't be present
pub fn maintain_abilities(&mut self, client: &client::Client) { pub fn maintain_abilities(&mut self, client: &client::Client, info: &HudInfo) {
use specs::WorldExt; use specs::WorldExt;
if let Some(active_abilities) = client if let Some(active_abilities) = client
.state() .state()
.ecs() .ecs()
.read_storage::<comp::ActiveAbilities>() .read_storage::<comp::ActiveAbilities>()
.get(client.entity()) .get(info.viewpoint_entity)
{ {
use common::comp::ability::AuxiliaryAbility; use common::comp::ability::AuxiliaryAbility;
for ((i, ability), hotbar_slot) in active_abilities for ((i, ability), hotbar_slot) in active_abilities
.auxiliary_set( .auxiliary_set(
client.inventories().get(client.entity()), client.inventories().get(info.viewpoint_entity),
client client
.state() .state()
.read_storage::<comp::SkillSet>() .read_storage::<comp::SkillSet>()
.get(client.entity()), .get(info.viewpoint_entity),
) )
.iter() .iter()
.enumerate() .enumerate()

View File

@ -2,7 +2,7 @@ use super::{
animate_by_pulse, get_quality_col, animate_by_pulse, get_quality_col,
img_ids::{Imgs, ImgsRot}, img_ids::{Imgs, ImgsRot},
item_imgs::ItemImgs, item_imgs::ItemImgs,
Show, Windows, TEXT_COLOR, HudInfo, Show, Windows, TEXT_COLOR,
}; };
use crate::ui::{fonts::Fonts, ImageFrame, ItemTooltip, ItemTooltipManager, ItemTooltipable}; use crate::ui::{fonts::Fonts, ImageFrame, ItemTooltip, ItemTooltipManager, ItemTooltipable};
use client::Client; use client::Client;
@ -51,6 +51,7 @@ pub struct LootScroller<'a> {
new_messages: &'a mut VecDeque<LootMessage>, new_messages: &'a mut VecDeque<LootMessage>,
client: &'a Client, client: &'a Client,
info: &'a HudInfo,
show: &'a Show, show: &'a Show,
imgs: &'a Imgs, imgs: &'a Imgs,
item_imgs: &'a ItemImgs, item_imgs: &'a ItemImgs,
@ -68,6 +69,7 @@ impl<'a> LootScroller<'a> {
pub fn new( pub fn new(
new_messages: &'a mut VecDeque<LootMessage>, new_messages: &'a mut VecDeque<LootMessage>,
client: &'a Client, client: &'a Client,
info: &'a HudInfo,
show: &'a Show, show: &'a Show,
imgs: &'a Imgs, imgs: &'a Imgs,
item_imgs: &'a ItemImgs, item_imgs: &'a ItemImgs,
@ -81,6 +83,7 @@ impl<'a> LootScroller<'a> {
Self { Self {
new_messages, new_messages,
client, client,
info,
show, show,
imgs, imgs,
item_imgs, item_imgs,
@ -143,6 +146,7 @@ impl<'a> Widget for LootScroller<'a> {
) )
}, },
self.client, self.client,
self.info,
self.imgs, self.imgs,
self.item_imgs, self.item_imgs,
self.pulse, self.pulse,

View File

@ -501,6 +501,8 @@ pub struct DebugInfo {
pub struct HudInfo { pub struct HudInfo {
pub is_aiming: bool, pub is_aiming: bool,
pub is_first_person: bool, pub is_first_person: bool,
pub viewpoint_entity: specs::Entity,
pub mutable_viewpoint: bool,
pub target_entity: Option<specs::Entity>, pub target_entity: Option<specs::Entity>,
pub selected_entity: Option<(specs::Entity, Instant)>, pub selected_entity: Option<(specs::Entity, Instant)>,
} }
@ -1278,7 +1280,7 @@ impl Hud {
let players = ecs.read_storage::<comp::Player>(); let players = ecs.read_storage::<comp::Player>();
let msm = ecs.read_resource::<MaterialStatManifest>(); let msm = ecs.read_resource::<MaterialStatManifest>();
let entities = ecs.entities(); let entities = ecs.entities();
let me = client.entity(); let me = info.viewpoint_entity;
let poises = ecs.read_storage::<comp::Poise>(); let poises = ecs.read_storage::<comp::Poise>();
let alignments = ecs.read_storage::<comp::Alignment>(); let alignments = ecs.read_storage::<comp::Alignment>();
let is_mount = ecs.read_storage::<Is<Mount>>(); let is_mount = ecs.read_storage::<Is<Mount>>();
@ -2585,7 +2587,7 @@ impl Hud {
// Bag button and nearby icons // Bag button and nearby icons
let ecs = client.state().ecs(); let ecs = client.state().ecs();
let entity = client.entity(); let entity = info.viewpoint_entity;
let stats = ecs.read_storage::<comp::Stats>(); let stats = ecs.read_storage::<comp::Stats>();
let skill_sets = ecs.read_storage::<comp::SkillSet>(); let skill_sets = ecs.read_storage::<comp::SkillSet>();
let buffs = ecs.read_storage::<comp::Buffs>(); let buffs = ecs.read_storage::<comp::Buffs>();
@ -2593,6 +2595,7 @@ impl Hud {
if let (Some(player_stats), Some(skill_set)) = (stats.get(entity), skill_sets.get(entity)) { if let (Some(player_stats), Some(skill_set)) = (stats.get(entity), skill_sets.get(entity)) {
match Buttons::new( match Buttons::new(
client, client,
&info,
self.show.bag, self.show.bag,
&self.imgs, &self.imgs,
&self.fonts, &self.fonts,
@ -2705,14 +2708,12 @@ impl Hud {
// Skillbar // Skillbar
// Get player stats // Get player stats
let ecs = client.state().ecs(); let ecs = client.state().ecs();
let entity = client.entity(); let entity = info.viewpoint_entity;
let healths = ecs.read_storage::<Health>(); let healths = ecs.read_storage::<Health>();
let inventories = ecs.read_storage::<comp::Inventory>(); let inventories = ecs.read_storage::<comp::Inventory>();
let energies = ecs.read_storage::<comp::Energy>(); let energies = ecs.read_storage::<comp::Energy>();
let skillsets = ecs.read_storage::<comp::SkillSet>(); let skillsets = ecs.read_storage::<comp::SkillSet>();
let active_abilities = ecs.read_storage::<comp::ActiveAbilities>(); let active_abilities = ecs.read_storage::<comp::ActiveAbilities>();
let character_states = ecs.read_storage::<comp::CharacterState>();
let controllers = ecs.read_storage::<comp::Controller>();
let bodies = ecs.read_storage::<comp::Body>(); let bodies = ecs.read_storage::<comp::Body>();
let poises = ecs.read_storage::<comp::Poise>(); let poises = ecs.read_storage::<comp::Poise>();
// Combo floater stuffs // Combo floater stuffs
@ -2722,25 +2723,16 @@ impl Hud {
}); });
self.floaters.combo_floater = self.floaters.combo_floater.filter(|f| f.timer > 0_f64); self.floaters.combo_floater = self.floaters.combo_floater.filter(|f| f.timer > 0_f64);
if let ( if let (Some(health), Some(inventory), Some(energy), Some(skillset), Some(body)) = (
Some(health),
Some(inventory),
Some(energy),
Some(skillset),
Some(body),
Some(_character_state),
Some(_controller),
) = (
healths.get(entity), healths.get(entity),
inventories.get(entity), inventories.get(entity),
energies.get(entity), energies.get(entity),
skillsets.get(entity), skillsets.get(entity),
bodies.get(entity), bodies.get(entity),
character_states.get(entity),
controllers.get(entity).map(|c| &c.inputs),
) { ) {
Skillbar::new( Skillbar::new(
client, client,
&info,
global_state, global_state,
&self.imgs, &self.imgs,
&self.item_imgs, &self.item_imgs,
@ -2775,8 +2767,8 @@ impl Hud {
Some(body), Some(body),
Some(poise), Some(poise),
) = ( ) = (
stats.get(client.entity()), stats.get(info.viewpoint_entity),
skill_sets.get(client.entity()), skill_sets.get(info.viewpoint_entity),
healths.get(entity), healths.get(entity),
energies.get(entity), energies.get(entity),
bodies.get(entity), bodies.get(entity),
@ -2784,6 +2776,7 @@ impl Hud {
) { ) {
match Bag::new( match Bag::new(
client, client,
&info,
global_state, global_state,
&self.imgs, &self.imgs,
&self.item_imgs, &self.item_imgs,
@ -2828,6 +2821,7 @@ impl Hud {
if self.show.trade { if self.show.trade {
if let Some(action) = Trade::new( if let Some(action) = Trade::new(
client, client,
&info,
&self.imgs, &self.imgs,
&self.item_imgs, &self.item_imgs,
&self.fonts, &self.fonts,
@ -2871,14 +2865,10 @@ impl Hud {
} }
// Buffs // Buffs
let ecs = client.state().ecs();
let entity = client.entity();
let health = ecs.read_storage::<Health>();
let energy = ecs.read_storage::<comp::Energy>();
if let (Some(player_buffs), Some(health), Some(energy)) = ( if let (Some(player_buffs), Some(health), Some(energy)) = (
buffs.get(client.entity()), buffs.get(info.viewpoint_entity),
health.get(entity), healths.get(entity),
energy.get(entity), energies.get(entity),
) { ) {
for event in BuffsBar::new( for event in BuffsBar::new(
&self.imgs, &self.imgs,
@ -2905,6 +2895,7 @@ impl Hud {
for event in Crafting::new( for event in Crafting::new(
//&self.show, //&self.show,
client, client,
&info,
&self.imgs, &self.imgs,
&self.fonts, &self.fonts,
&*i18n, &*i18n,
@ -3038,6 +3029,7 @@ impl Hud {
LootScroller::new( LootScroller::new(
&mut self.new_loot_messages, &mut self.new_loot_messages,
client, client,
&info,
&self.show, &self.show,
&self.imgs, &self.imgs,
&self.item_imgs, &self.item_imgs,
@ -3107,49 +3099,45 @@ impl Hud {
if self.show.social { if self.show.social {
let ecs = client.state().ecs(); let ecs = client.state().ecs();
let _stats = ecs.read_storage::<comp::Stats>(); let _stats = ecs.read_storage::<comp::Stats>();
let me = client.entity(); for event in Social::new(
if let Some(_stats) = stats.get(me) { &self.show,
for event in Social::new( client,
&self.show, &self.imgs,
client, &self.fonts,
&self.imgs, i18n,
&self.fonts, info.selected_entity,
i18n, &self.rot_imgs,
info.selected_entity, tooltip_manager,
&self.rot_imgs, )
tooltip_manager, .set(self.ids.social_window, ui_widgets)
) {
.set(self.ids.social_window, ui_widgets) match event {
{ social::Event::Close => {
match event { self.show.social(false);
social::Event::Close => { if !self.show.bag {
self.show.social(false); self.show.want_grab = true;
if !self.show.bag { self.force_ungrab = false;
self.show.want_grab = true; } else {
self.force_ungrab = false; self.force_ungrab = true
} else { };
self.force_ungrab = true },
}; social::Event::Focus(widget_id) => {
}, self.to_focus = Some(Some(widget_id));
social::Event::Focus(widget_id) => { },
self.to_focus = Some(Some(widget_id)); social::Event::Invite(uid) => events.push(Event::InviteMember(uid)),
}, social::Event::SearchPlayers(search_key) => {
social::Event::Invite(uid) => events.push(Event::InviteMember(uid)), self.show.search_social_players(search_key)
social::Event::SearchPlayers(search_key) => { },
self.show.search_social_players(search_key)
},
}
} }
} }
} }
// Diary // Diary
if self.show.diary { if self.show.diary {
let entity = client.entity(); let entity = info.viewpoint_entity;
let skill_sets = ecs.read_storage::<comp::SkillSet>(); let skill_sets = ecs.read_storage::<comp::SkillSet>();
if let ( if let (
Some(skill_set), Some(skill_set),
Some(active_abilities),
Some(inventory), Some(inventory),
Some(health), Some(health),
Some(energy), Some(energy),
@ -3157,7 +3145,6 @@ impl Hud {
Some(poise), Some(poise),
) = ( ) = (
skill_sets.get(entity), skill_sets.get(entity),
active_abilities.get(entity),
inventories.get(entity), inventories.get(entity),
healths.get(entity), healths.get(entity),
energies.get(entity), energies.get(entity),
@ -3169,7 +3156,7 @@ impl Hud {
client, client,
global_state, global_state,
skill_set, skill_set,
active_abilities, active_abilities.get(entity).unwrap_or(&Default::default()),
inventory, inventory,
health, health,
energy, energy,
@ -3386,7 +3373,7 @@ impl Hud {
) = (a, b) ) = (a, b)
{ {
if let Some(item) = inventories if let Some(item) = inventories
.get(client.entity()) .get(info.viewpoint_entity)
.and_then(|inv| inv.get(slot)) .and_then(|inv| inv.get(slot))
{ {
self.hotbar.add_inventory_link(h, item); self.hotbar.add_inventory_link(h, item);
@ -3423,7 +3410,7 @@ impl Hud {
events.push(Event::ChangeAbility(index, ability)); events.push(Event::ChangeAbility(index, ability));
}, },
(AbilitySlot::Slot(a), AbilitySlot::Slot(b)) => { (AbilitySlot::Slot(a), AbilitySlot::Slot(b)) => {
let me = client.entity(); let me = info.viewpoint_entity;
if let Some(active_abilities) = active_abilities.get(me) { if let Some(active_abilities) = active_abilities.get(me) {
let ability_a = active_abilities let ability_a = active_abilities
.auxiliary_set(inventories.get(me), skill_sets.get(me)) .auxiliary_set(inventories.get(me), skill_sets.get(me))
@ -3447,7 +3434,7 @@ impl Hud {
} else if let (Inventory(i), Crafting(c)) = (a, b) { } else if let (Inventory(i), Crafting(c)) = (a, b) {
// Add item to crafting input // Add item to crafting input
if inventories if inventories
.get(client.entity()) .get(info.viewpoint_entity)
.and_then(|inv| inv.get(i.slot)) .and_then(|inv| inv.get(i.slot))
.map_or(false, |item| { .map_or(false, |item| {
(c.requirement)(item, client.component_recipe_book(), c.info) (c.requirement)(item, client.component_recipe_book(), c.info)
@ -3508,7 +3495,7 @@ impl Hud {
}); });
} else if let (Inventory(i), Hotbar(h)) = (a, b) { } else if let (Inventory(i), Hotbar(h)) = (a, b) {
if let Some(item) = inventories if let Some(item) = inventories
.get(client.entity()) .get(info.viewpoint_entity)
.and_then(|inv| inv.get(i.slot)) .and_then(|inv| inv.get(i.slot))
{ {
self.hotbar.add_inventory_link(h, item); self.hotbar.add_inventory_link(h, item);
@ -3545,7 +3532,7 @@ impl Hud {
events.push(Event::ChangeAbility(index, ability)); events.push(Event::ChangeAbility(index, ability));
}, },
(AbilitySlot::Slot(a), AbilitySlot::Slot(b)) => { (AbilitySlot::Slot(a), AbilitySlot::Slot(b)) => {
let me = client.entity(); let me = info.viewpoint_entity;
if let Some(active_abilities) = active_abilities.get(me) { if let Some(active_abilities) = active_abilities.get(me) {
let ability_a = active_abilities let ability_a = active_abilities
.auxiliary_set(inventories.get(me), skill_sets.get(me)) .auxiliary_set(inventories.get(me), skill_sets.get(me))
@ -3592,7 +3579,7 @@ impl Hud {
// Used from hotbar // Used from hotbar
self.hotbar.get(h).map(|s| match s { self.hotbar.get(h).map(|s| match s {
hotbar::SlotContents::Inventory(i, _) => { hotbar::SlotContents::Inventory(i, _) => {
if let Some(inv) = inventories.get(client.entity()) { if let Some(inv) = inventories.get(info.viewpoint_entity) {
// If the item in the inactive main hand is the same as the item // If the item in the inactive main hand is the same as the item
// pressed in the hotbar, then swap active and inactive hands // pressed in the hotbar, then swap active and inactive hands
// instead of looking for // instead of looking for
@ -3643,7 +3630,7 @@ impl Hud {
}; };
} }
let who = match ecs let who = match ecs
.uid_from_entity(client.entity()) .uid_from_entity(info.viewpoint_entity)
.and_then(|uid| trade.which_party(uid)) .and_then(|uid| trade.which_party(uid))
{ {
Some(who) => who, Some(who) => who,
@ -3752,7 +3739,7 @@ impl Hud {
}, },
} }
} }
self.hotbar.maintain_abilities(client); self.hotbar.maintain_abilities(client, &info);
// Temporary Example Quest // Temporary Example Quest
let arrow_ani = (self.pulse * 4.0/* speed factor */).cos() * 0.5 + 0.8; //Animation timer let arrow_ani = (self.pulse * 4.0/* speed factor */).cos() * 0.5 + 0.8; //Animation timer

View File

@ -2,8 +2,8 @@ use super::{
hotbar, hotbar,
img_ids::{Imgs, ImgsRot}, img_ids::{Imgs, ImgsRot},
item_imgs::ItemImgs, item_imgs::ItemImgs,
slots, util, BarNumbers, ShortcutNumbers, BLACK, CRITICAL_HP_COLOR, HP_COLOR, LOW_HP_COLOR, slots, util, BarNumbers, HudInfo, ShortcutNumbers, BLACK, CRITICAL_HP_COLOR, HP_COLOR,
QUALITY_EPIC, STAMINA_COLOR, TEXT_COLOR, UI_HIGHLIGHT_0, LOW_HP_COLOR, QUALITY_EPIC, STAMINA_COLOR, TEXT_COLOR, UI_HIGHLIGHT_0,
}; };
use crate::{ use crate::{
game_input::GameInput, game_input::GameInput,
@ -242,6 +242,7 @@ fn slot_entries(state: &State, slot_offset: f64) -> [SlotEntry; 10] {
#[derive(WidgetCommon)] #[derive(WidgetCommon)]
pub struct Skillbar<'a> { pub struct Skillbar<'a> {
client: &'a Client, client: &'a Client,
info: &'a HudInfo,
global_state: &'a GlobalState, global_state: &'a GlobalState,
imgs: &'a Imgs, imgs: &'a Imgs,
item_imgs: &'a ItemImgs, item_imgs: &'a ItemImgs,
@ -271,6 +272,7 @@ impl<'a> Skillbar<'a> {
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
pub fn new( pub fn new(
client: &'a Client, client: &'a Client,
info: &'a HudInfo,
global_state: &'a GlobalState, global_state: &'a GlobalState,
imgs: &'a Imgs, imgs: &'a Imgs,
item_imgs: &'a ItemImgs, item_imgs: &'a ItemImgs,
@ -295,6 +297,7 @@ impl<'a> Skillbar<'a> {
) -> Self { ) -> Self {
Self { Self {
client, client,
info,
global_state, global_state,
imgs, imgs,
item_imgs, item_imgs,
@ -576,6 +579,7 @@ impl<'a> Skillbar<'a> {
) )
}, },
self.client, self.client,
self.info,
self.imgs, self.imgs,
self.item_imgs, self.item_imgs,
self.pulse, self.pulse,

View File

@ -31,7 +31,7 @@ use super::{
img_ids::{Imgs, ImgsRot}, img_ids::{Imgs, ImgsRot},
item_imgs::ItemImgs, item_imgs::ItemImgs,
slots::{SlotKind, SlotManager, TradeSlot}, slots::{SlotKind, SlotManager, TradeSlot},
Hud, Show, TradeAmountInput, TEXT_COLOR, TEXT_GRAY_COLOR, UI_HIGHLIGHT_0, UI_MAIN, Hud, HudInfo, Show, TradeAmountInput, TEXT_COLOR, TEXT_GRAY_COLOR, UI_HIGHLIGHT_0, UI_MAIN,
}; };
use std::borrow::Cow; use std::borrow::Cow;
@ -75,6 +75,7 @@ widget_ids! {
#[derive(WidgetCommon)] #[derive(WidgetCommon)]
pub struct Trade<'a> { pub struct Trade<'a> {
client: &'a Client, client: &'a Client,
info: &'a HudInfo,
imgs: &'a Imgs, imgs: &'a Imgs,
item_imgs: &'a ItemImgs, item_imgs: &'a ItemImgs,
fonts: &'a Fonts, fonts: &'a Fonts,
@ -92,6 +93,7 @@ pub struct Trade<'a> {
impl<'a> Trade<'a> { impl<'a> Trade<'a> {
pub fn new( pub fn new(
client: &'a Client, client: &'a Client,
info: &'a HudInfo,
imgs: &'a Imgs, imgs: &'a Imgs,
item_imgs: &'a ItemImgs, item_imgs: &'a ItemImgs,
fonts: &'a Fonts, fonts: &'a Fonts,
@ -105,6 +107,7 @@ impl<'a> Trade<'a> {
) -> Self { ) -> Self {
Self { Self {
client, client,
info,
imgs, imgs,
item_imgs, item_imgs,
fonts, fonts,
@ -313,6 +316,7 @@ impl<'a> Trade<'a> {
) )
}, },
self.client, self.client,
self.info,
self.imgs, self.imgs,
self.item_imgs, self.item_imgs,
self.pulse, self.pulse,

View File

@ -16,7 +16,7 @@ const CLIPPING_MODE_DISTANCE: f32 = 20.0;
pub const MIN_ZOOM: f32 = 0.1; pub const MIN_ZOOM: f32 = 0.1;
// Possible TODO: Add more modes // Possible TODO: Add more modes
#[derive(PartialEq, Clone, Copy, Eq, Hash)] #[derive(PartialEq, Debug, Clone, Copy, Eq, Hash)]
pub enum CameraMode { pub enum CameraMode {
FirstPerson = 0, FirstPerson = 0,
ThirdPerson = 1, ThirdPerson = 1,
@ -130,7 +130,11 @@ fn clamp_and_modulate(ori: Vec3<f32>) -> Vec3<f32> {
/// e = floor(ln(near/(far - near))/ln(2)) /// e = floor(ln(near/(far - near))/ln(2))
/// db/dz = 2^(2-e) / ((1 / far - 1 / near) * (far)^2) /// db/dz = 2^(2-e) / ((1 / far - 1 / near) * (far)^2)
/// ``` /// ```
///CameraMode::ThirdPerson ///
/// Then the maximum precision you can safely use to get a change in the
/// integer representation of the mantissa (assuming 32-bit floating points)
/// is around:
///
/// ```ignore /// ```ignore
/// abs(2^(-23) / (db/dz)). /// abs(2^(-23) / (db/dz)).
/// ``` /// ```
@ -721,10 +725,8 @@ impl Camera {
/// Cycle the camera to its next valid mode. If is_admin is false then only /// Cycle the camera to its next valid mode. If is_admin is false then only
/// modes which are accessible without admin access will be cycled to. /// modes which are accessible without admin access will be cycled to.
pub fn next_mode(&mut self, is_admin: bool, is_spectator: bool) { pub fn next_mode(&mut self, is_admin: bool, has_target: bool) {
if is_spectator && is_admin { if has_target {
self.set_mode(CameraMode::Freefly);
} else {
self.set_mode(match self.mode { self.set_mode(match self.mode {
CameraMode::ThirdPerson => CameraMode::FirstPerson, CameraMode::ThirdPerson => CameraMode::FirstPerson,
CameraMode::FirstPerson => { CameraMode::FirstPerson => {
@ -736,6 +738,8 @@ impl Camera {
}, },
CameraMode::Freefly => CameraMode::ThirdPerson, CameraMode::Freefly => CameraMode::ThirdPerson,
}); });
} else {
self.set_mode(CameraMode::Freefly);
} }
} }

View File

@ -707,7 +707,7 @@ impl FigureMgr {
// Get player position. // Get player position.
let player_pos = ecs let player_pos = ecs
.read_storage::<Pos>() .read_storage::<Pos>()
.get(scene_data.player_entity) .get(scene_data.viewpoint_entity)
.map_or(anim::vek::Vec3::zero(), |pos| anim::vek::Vec3::from(pos.0)); .map_or(anim::vek::Vec3::zero(), |pos| anim::vek::Vec3::from(pos.0));
let visible_aabb = anim::vek::Aabb { let visible_aabb = anim::vek::Aabb {
min: player_pos - 2.0, min: player_pos - 2.0,
@ -716,7 +716,7 @@ impl FigureMgr {
let camera_mode = camera.get_mode(); let camera_mode = camera.get_mode();
let character_state_storage = state.read_storage::<CharacterState>(); let character_state_storage = state.read_storage::<CharacterState>();
let slow_jobs = state.slow_job_pool(); let slow_jobs = state.slow_job_pool();
let character_state = character_state_storage.get(scene_data.player_entity); let character_state = character_state_storage.get(scene_data.viewpoint_entity);
let focus_pos = anim::vek::Vec3::<f32>::from(camera.get_focus_pos()); let focus_pos = anim::vek::Vec3::<f32>::from(camera.get_focus_pos());
@ -771,13 +771,13 @@ impl FigureMgr {
let rel_vel = anim::vek::Vec3::<f32>::from(vel.0 - physics.ground_vel); let rel_vel = anim::vek::Vec3::<f32>::from(vel.0 - physics.ground_vel);
let look_dir = controller.map(|c| c.inputs.look_dir).unwrap_or_default(); let look_dir = controller.map(|c| c.inputs.look_dir).unwrap_or_default();
let is_player = scene_data.player_entity == entity; let is_viewpoint = scene_data.viewpoint_entity == entity;
let player_camera_mode = if is_player { let viewpoint_camera_mode = if is_viewpoint {
camera_mode camera_mode
} else { } else {
CameraMode::default() CameraMode::default()
}; };
let player_character_state = if is_player { character_state } else { None }; let viewpoint_character_state = if is_viewpoint { character_state } else { None };
let (pos, ori) = interpolated let (pos, ori) = interpolated
.map(|i| { .map(|i| {
@ -941,7 +941,7 @@ impl FigureMgr {
dt, dt,
_lpindex: lpindex, _lpindex: lpindex,
_visible: in_frustum, _visible: in_frustum,
is_player, is_player: is_viewpoint,
_camera: camera, _camera: camera,
terrain, terrain,
ground_vel: physics.ground_vel, ground_vel: physics.ground_vel,
@ -956,8 +956,8 @@ impl FigureMgr {
inventory, inventory,
(), (),
tick, tick,
player_camera_mode, viewpoint_camera_mode,
player_character_state, viewpoint_character_state,
&slow_jobs, &slow_jobs,
None, None,
); );
@ -1856,8 +1856,8 @@ impl FigureMgr {
inventory, inventory,
(), (),
tick, tick,
player_camera_mode, viewpoint_camera_mode,
player_character_state, viewpoint_character_state,
&slow_jobs, &slow_jobs,
None, None,
); );
@ -2056,8 +2056,8 @@ impl FigureMgr {
inventory, inventory,
(), (),
tick, tick,
player_camera_mode, viewpoint_camera_mode,
player_character_state, viewpoint_character_state,
&slow_jobs, &slow_jobs,
None, None,
); );
@ -2373,8 +2373,8 @@ impl FigureMgr {
inventory, inventory,
(), (),
tick, tick,
player_camera_mode, viewpoint_camera_mode,
player_character_state, viewpoint_character_state,
&slow_jobs, &slow_jobs,
None, None,
); );
@ -2730,8 +2730,8 @@ impl FigureMgr {
inventory, inventory,
(), (),
tick, tick,
player_camera_mode, viewpoint_camera_mode,
player_character_state, viewpoint_character_state,
&slow_jobs, &slow_jobs,
None, None,
); );
@ -2834,8 +2834,8 @@ impl FigureMgr {
inventory, inventory,
(), (),
tick, tick,
player_camera_mode, viewpoint_camera_mode,
player_character_state, viewpoint_character_state,
&slow_jobs, &slow_jobs,
None, None,
); );
@ -2917,8 +2917,8 @@ impl FigureMgr {
inventory, inventory,
(), (),
tick, tick,
player_camera_mode, viewpoint_camera_mode,
player_character_state, viewpoint_character_state,
&slow_jobs, &slow_jobs,
None, None,
); );
@ -3444,8 +3444,8 @@ impl FigureMgr {
inventory, inventory,
(), (),
tick, tick,
player_camera_mode, viewpoint_camera_mode,
player_character_state, viewpoint_character_state,
&slow_jobs, &slow_jobs,
None, None,
); );
@ -3531,8 +3531,8 @@ impl FigureMgr {
inventory, inventory,
(), (),
tick, tick,
player_camera_mode, viewpoint_camera_mode,
player_character_state, viewpoint_character_state,
&slow_jobs, &slow_jobs,
None, None,
); );
@ -3710,8 +3710,8 @@ impl FigureMgr {
inventory, inventory,
(), (),
tick, tick,
player_camera_mode, viewpoint_camera_mode,
player_character_state, viewpoint_character_state,
&slow_jobs, &slow_jobs,
None, None,
); );
@ -4002,8 +4002,8 @@ impl FigureMgr {
inventory, inventory,
(), (),
tick, tick,
player_camera_mode, viewpoint_camera_mode,
player_character_state, viewpoint_character_state,
&slow_jobs, &slow_jobs,
None, None,
); );
@ -4325,8 +4325,8 @@ impl FigureMgr {
inventory, inventory,
(), (),
tick, tick,
player_camera_mode, viewpoint_camera_mode,
player_character_state, viewpoint_character_state,
&slow_jobs, &slow_jobs,
None, None,
); );
@ -4408,8 +4408,8 @@ impl FigureMgr {
inventory, inventory,
(), (),
tick, tick,
player_camera_mode, viewpoint_camera_mode,
player_character_state, viewpoint_character_state,
&slow_jobs, &slow_jobs,
None, None,
); );
@ -5030,8 +5030,8 @@ impl FigureMgr {
inventory, inventory,
(), (),
tick, tick,
player_camera_mode, viewpoint_camera_mode,
player_character_state, viewpoint_character_state,
&slow_jobs, &slow_jobs,
None, None,
); );
@ -5271,8 +5271,8 @@ impl FigureMgr {
inventory, inventory,
(), (),
tick, tick,
player_camera_mode, viewpoint_camera_mode,
player_character_state, viewpoint_character_state,
&slow_jobs, &slow_jobs,
None, None,
); );
@ -5393,8 +5393,8 @@ impl FigureMgr {
inventory, inventory,
(), (),
tick, tick,
player_camera_mode, viewpoint_camera_mode,
player_character_state, viewpoint_character_state,
&slow_jobs, &slow_jobs,
item_key, item_key,
); );
@ -5453,8 +5453,8 @@ impl FigureMgr {
inventory, inventory,
Arc::clone(vol), Arc::clone(vol),
tick, tick,
player_camera_mode, viewpoint_camera_mode,
player_character_state, viewpoint_character_state,
&slow_jobs, &slow_jobs,
None, None,
); );
@ -5484,8 +5484,8 @@ impl FigureMgr {
inventory, inventory,
(), (),
tick, tick,
player_camera_mode, viewpoint_camera_mode,
player_character_state, viewpoint_character_state,
&slow_jobs, &slow_jobs,
None, None,
) )

View File

@ -116,7 +116,8 @@ pub struct Scene {
pub struct SceneData<'a> { pub struct SceneData<'a> {
pub client: &'a Client, pub client: &'a Client,
pub state: &'a State, pub state: &'a State,
pub player_entity: specs::Entity, pub viewpoint_entity: specs::Entity,
pub mutable_viewpoint: bool,
pub target_entity: Option<specs::Entity>, pub target_entity: Option<specs::Entity>,
pub loaded_distance: f32, pub loaded_distance: f32,
pub view_distance: u32, pub view_distance: u32,
@ -502,71 +503,104 @@ impl Scene {
let dt = ecs.fetch::<DeltaTime>().0; let dt = ecs.fetch::<DeltaTime>().0;
let player_pos = ecs let positions = ecs.read_storage::<comp::Pos>();
.read_storage::<comp::Pos>()
.get(scene_data.player_entity)
.map_or(Vec3::zero(), |pos| pos.0);
let player_rolling = ecs let viewpoint_pos = if let Some(viewpoint_pos) =
.read_storage::<comp::CharacterState>() positions.get(scene_data.viewpoint_entity).map(|pos| pos.0)
.get(scene_data.player_entity) {
.map_or(false, |cs| cs.is_dodge()); let viewpoint_ori = ecs
.read_storage::<comp::Ori>()
.get(scene_data.viewpoint_entity)
.map_or(Quaternion::identity(), |ori| ori.to_quat());
let is_running = ecs let viewpoint_rolling = ecs
.read_storage::<comp::Vel>() .read_storage::<comp::CharacterState>()
.get(scene_data.player_entity) .get(scene_data.viewpoint_entity)
.map(|v| v.0.magnitude_squared() > RUNNING_THRESHOLD.powi(2)) .map_or(false, |cs| cs.is_dodge());
.unwrap_or(false);
let on_ground = ecs let is_running = ecs
.read_storage::<comp::PhysicsState>() .read_storage::<comp::Vel>()
.get(scene_data.player_entity) .get(scene_data.viewpoint_entity)
.map(|p| p.on_ground.is_some()); .map(|v| v.0.magnitude_squared() > RUNNING_THRESHOLD.powi(2))
.unwrap_or(false);
let (player_height, player_eye_height) = scene_data let on_ground = ecs
.state .read_storage::<comp::PhysicsState>()
.ecs() .get(scene_data.viewpoint_entity)
.read_storage::<comp::Body>() .map(|p| p.on_ground.is_some());
.get(scene_data.player_entity)
.map_or((1.0, 0.0), |b| (b.height(), b.eye_height()));
// Add the analog input to camera let (viewpoint_height, viewpoint_eye_height) = scene_data
self.camera .state
.rotate_by(Vec3::from([self.camera_input_state.x, 0.0, 0.0])); .ecs()
self.camera .read_storage::<comp::Body>()
.rotate_by(Vec3::from([0.0, self.camera_input_state.y, 0.0])); .get(scene_data.viewpoint_entity)
.map_or((1.0, 0.0), |b| (b.height(), b.eye_height()));
// Alter camera position to match player. if scene_data.mutable_viewpoint || matches!(self.camera.get_mode(), CameraMode::Freefly)
let tilt = self.camera.get_orientation().y; {
let dist = self.camera.get_distance(); // Add the analog input to camera if it's a mutable viewpoint
self.camera
.rotate_by(Vec3::from([self.camera_input_state.x, 0.0, 0.0]));
self.camera
.rotate_by(Vec3::from([0.0, self.camera_input_state.y, 0.0]));
} else {
// Otherwise set the cameras rotation to the viewpoints
let q = viewpoint_ori;
let sinr_cosp = 2.0 * (q.w * q.x + q.y * q.z);
let cosr_cosp = 1.0 - 2.0 * (q.x * q.x + q.y * q.y);
let roll = sinr_cosp.atan2(cosr_cosp);
let up = match self.camera.get_mode() { let sinp = 2.0 * (q.w * q.y - q.z * q.x);
CameraMode::FirstPerson => { let pitch = if sinp.abs() >= 1.0 {
if player_rolling { std::f32::consts::FRAC_PI_2.copysign(sinp)
player_height * 0.42
} else if is_running && on_ground.unwrap_or(false) {
player_eye_height + (scene_data.state.get_time() as f32 * 17.0).sin() * 0.05
} else { } else {
player_eye_height sinp.asin()
} };
},
CameraMode::ThirdPerson if scene_data.is_aiming => player_height * 1.16,
CameraMode::ThirdPerson => player_eye_height,
CameraMode::Freefly => 0.0,
};
match self.camera.get_mode() { let siny_cosp = 2.0 * (q.w * q.z + q.x * q.y);
CameraMode::FirstPerson | CameraMode::ThirdPerson => { let cosy_cosp = 1.0 - 2.0 * (q.y * q.y + q.z * q.z);
self.camera.set_focus_pos( let yaw = siny_cosp.atan2(cosy_cosp);
player_pos + Vec3::unit_z() * (up - tilt.min(0.0).sin() * dist * 0.6),
);
},
CameraMode::Freefly => {},
};
// Tick camera for interpolation. self.camera
self.camera .set_orientation_instant(Vec3::new(yaw, pitch, -roll));
.update(scene_data.state.get_time(), dt, scene_data.mouse_smoothing); }
// Alter camera position to match player.
let tilt = self.camera.get_orientation().y;
let dist = self.camera.get_distance();
let up = match self.camera.get_mode() {
CameraMode::FirstPerson => {
if viewpoint_rolling {
viewpoint_height * 0.42
} else if is_running && on_ground.unwrap_or(false) {
viewpoint_eye_height
+ (scene_data.state.get_time() as f32 * 17.0).sin() * 0.05
} else {
viewpoint_eye_height
}
},
CameraMode::ThirdPerson if scene_data.is_aiming => viewpoint_height * 1.16,
CameraMode::ThirdPerson => viewpoint_eye_height,
CameraMode::Freefly => 0.0,
};
match self.camera.get_mode() {
CameraMode::FirstPerson | CameraMode::ThirdPerson => {
self.camera.set_focus_pos(
viewpoint_pos + Vec3::unit_z() * (up - tilt.min(0.0).sin() * dist * 0.6),
);
},
CameraMode::Freefly => {},
};
// Tick camera for interpolation.
self.camera
.update(scene_data.state.get_time(), dt, scene_data.mouse_smoothing);
viewpoint_pos
} else {
Vec3::zero()
};
// Compute camera matrices. // Compute camera matrices.
self.camera.compute_dependents(&*scene_data.state.terrain()); self.camera.compute_dependents(&*scene_data.state.terrain());
@ -617,7 +651,7 @@ impl Scene {
.filter(|(pos, _, light_anim, h)| { .filter(|(pos, _, light_anim, h)| {
light_anim.col != Rgb::zero() light_anim.col != Rgb::zero()
&& light_anim.strength > 0.0 && light_anim.strength > 0.0
&& (pos.0.distance_squared(player_pos) as f32) && (pos.0.distance_squared(viewpoint_pos) as f32)
< loaded_distance.powi(2) + LIGHT_DIST_RADIUS < loaded_distance.powi(2) + LIGHT_DIST_RADIUS
&& h.map_or(true, |h| !h.is_dead) && h.map_or(true, |h| !h.is_dead)
}) })
@ -632,7 +666,7 @@ impl Scene {
.map(|el| el.light.with_strength((el.fadeout)(el.timeout))), .map(|el| el.light.with_strength((el.fadeout)(el.timeout))),
), ),
); );
lights.sort_by_key(|light| light.get_pos().distance_squared(player_pos) as i32); lights.sort_by_key(|light| light.get_pos().distance_squared(viewpoint_pos) as i32);
lights.truncate(MAX_LIGHT_COUNT); lights.truncate(MAX_LIGHT_COUNT);
renderer.update_consts(&mut self.data.lights, lights); renderer.update_consts(&mut self.data.lights, lights);
@ -657,7 +691,7 @@ impl Scene {
.join() .join()
.filter(|(_, _, _, _, health)| !health.is_dead) .filter(|(_, _, _, _, health)| !health.is_dead)
.filter(|(pos, _, _, _, _)| { .filter(|(pos, _, _, _, _)| {
(pos.0.distance_squared(player_pos) as f32) (pos.0.distance_squared(viewpoint_pos) as f32)
< (loaded_distance.min(SHADOW_MAX_DIST) + SHADOW_DIST_RADIUS).powi(2) < (loaded_distance.min(SHADOW_MAX_DIST) + SHADOW_DIST_RADIUS).powi(2)
}) })
.map(|(pos, interpolated, scale, _, _)| { .map(|(pos, interpolated, scale, _, _)| {
@ -668,7 +702,7 @@ impl Scene {
) )
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
shadows.sort_by_key(|shadow| shadow.get_pos().distance_squared(player_pos) as i32); shadows.sort_by_key(|shadow| shadow.get_pos().distance_squared(viewpoint_pos) as i32);
shadows.truncate(MAX_SHADOW_COUNT); shadows.truncate(MAX_SHADOW_COUNT);
renderer.update_consts(&mut self.data.shadows, &shadows); renderer.update_consts(&mut self.data.shadows, &shadows);
@ -1139,7 +1173,7 @@ impl Scene {
self.sfx_mgr.maintain( self.sfx_mgr.maintain(
audio, audio,
scene_data.state, scene_data.state,
scene_data.player_entity, scene_data.viewpoint_entity,
&self.camera, &self.camera,
&self.terrain, &self.terrain,
client, client,

View File

@ -1166,7 +1166,7 @@ impl ParticleMgr {
let time = scene_data.state.get_time(); let time = scene_data.state.get_time();
let player_pos = scene_data let player_pos = scene_data
.state .state
.read_component_copied::<Interpolated>(scene_data.player_entity) .read_component_copied::<Interpolated>(scene_data.viewpoint_entity)
.map(|i| i.pos) .map(|i| i.pos)
.unwrap_or_default(); .unwrap_or_default();
let player_chunk = player_pos.xy().map2(TerrainChunk::RECT_SIZE, |e, sz| { let player_chunk = player_pos.xy().map2(TerrainChunk::RECT_SIZE, |e, sz| {

View File

@ -88,6 +88,7 @@ pub struct SessionState {
is_aiming: bool, is_aiming: bool,
target_entity: Option<specs::Entity>, target_entity: Option<specs::Entity>,
selected_entity: Option<(specs::Entity, std::time::Instant)>, selected_entity: Option<(specs::Entity, std::time::Instant)>,
viewpoint_entity: Option<specs::Entity>,
interactable: Option<Interactable>, interactable: Option<Interactable>,
#[cfg(not(target_os = "macos"))] #[cfg(not(target_os = "macos"))]
mumble_link: SharedLink, mumble_link: SharedLink,
@ -149,6 +150,7 @@ impl SessionState {
is_aiming: false, is_aiming: false,
target_entity: None, target_entity: None,
selected_entity: None, selected_entity: None,
viewpoint_entity: None,
interactable: None, interactable: None,
#[cfg(not(target_os = "macos"))] #[cfg(not(target_os = "macos"))]
mumble_link, mumble_link,
@ -162,6 +164,14 @@ impl SessionState {
self.key_state.auto_walk = false; self.key_state.auto_walk = false;
} }
/// Gets the entity that is the current viewpoint, and a bool if the client
/// is allowed to edit it's data.
fn viewpoint_entity(&self) -> (specs::Entity, bool) {
self.viewpoint_entity
.map(|e| (e, false))
.unwrap_or_else(|| (self.client.borrow().entity(), true))
}
/// Tick the session (and the client attached to it). /// Tick the session (and the client attached to it).
fn tick( fn tick(
&mut self, &mut self,
@ -548,12 +558,11 @@ impl PlayState for SessionState {
if !self.inputs_state.insert(input) { if !self.inputs_state.insert(input) {
self.inputs_state.remove(&input); self.inputs_state.remove(&input);
} }
match input { match input {
GameInput::Primary => { GameInput::Primary => {
let mut client = self.client.borrow_mut(); let mut client = self.client.borrow_mut();
// Mine and build targets can be the same block. make building take // Mine and build targets can be the same block. make building
// precedence. // take precedence.
// Order of precedence: build, then mining, then attack. // Order of precedence: build, then mining, then attack.
if let Some(build_target) = build_target.filter(|bt| { if let Some(build_target) = build_target.filter(|bt| {
state && can_build && nearest_block_dist == Some(bt.distance) state && can_build && nearest_block_dist == Some(bt.distance)
@ -890,16 +899,18 @@ impl PlayState for SessionState {
// Prevent accessing camera modes which aren't available in // Prevent accessing camera modes which aren't available in
// multiplayer unless you are an // multiplayer unless you are an
// admin. This is an easily bypassed clientside check. // admin. This is an easily bypassed clientside check.
// The server should do its own filtering of which entities are sent // The server should do its own filtering of which entities are
// to clients to prevent abuse. // sent to clients to
// prevent abuse.
let camera = self.scene.camera_mut(); let camera = self.scene.camera_mut();
let client = self.client.borrow(); let client = self.client.borrow();
camera.next_mode( camera.next_mode(
client.is_moderator(), client.is_moderator(),
client client
.presence() .presence()
.map(|presence| presence == PresenceKind::Spectator) .map(|presence| presence != PresenceKind::Spectator)
.unwrap_or(false), .unwrap_or(true)
|| self.viewpoint_entity.is_some(),
); );
}, },
GameInput::Select => { GameInput::Select => {
@ -920,6 +931,24 @@ impl PlayState for SessionState {
client.decline_invite(); client.decline_invite();
} }
}, },
GameInput::SpectateViewpoint if state => {
if self.viewpoint_entity.is_some() {
self.viewpoint_entity = None;
self.scene.camera_mut().set_mode(CameraMode::Freefly);
} else if let Some(interactable) = self.interactable {
if self.scene.camera().get_mode() == CameraMode::Freefly {
match interactable {
Interactable::Block(_, _, _) => {},
Interactable::Entity(entity) => {
self.viewpoint_entity = Some(entity);
self.scene
.camera_mut()
.set_mode(CameraMode::FirstPerson);
},
}
}
}
},
_ => {}, _ => {},
} }
}, },
@ -949,97 +978,121 @@ impl PlayState for SessionState {
} }
} }
// If auto-gliding, point camera into the wind if self.viewpoint_entity.map_or(false, |entity| {
if let Some(dir) = self !self
.auto_walk .client
.then_some(self.client.borrow()) .borrow()
.filter(|client| client.is_gliding()) .state()
.and_then(|client| { .ecs()
let ecs = client.state().ecs(); .read_storage::<Pos>()
let entity = client.entity(); .contains(entity)
let fluid = ecs }) {
.read_storage::<comp::PhysicsState>() self.viewpoint_entity = None;
.get(entity)? self.scene.camera_mut().set_mode(CameraMode::Freefly);
.in_fluid?;
ecs.read_storage::<Vel>()
.get(entity)
.map(|vel| fluid.relative_flow(vel).0)
.map(|rel_flow| {
let is_wind_downwards = rel_flow.dot(Vec3::unit_z()).is_sign_negative();
if !self.free_look {
if is_wind_downwards {
self.scene.camera().forward_xy().into()
} else {
let windwards = rel_flow
* self
.scene
.camera()
.forward_xy()
.dot(rel_flow.xy())
.signum();
Plane::from(Dir::new(self.scene.camera().right()))
.projection(windwards)
}
} else if is_wind_downwards {
Vec3::from(-rel_flow.xy())
} else {
-rel_flow
}
})
.and_then(Dir::from_unnormalized)
})
{
self.key_state.auto_walk = false;
self.inputs.move_dir = Vec2::zero();
self.inputs.look_dir = dir;
} else {
self.key_state.auto_walk = self.auto_walk;
if !self.free_look {
self.walk_forward_dir = self.scene.camera().forward_xy();
self.walk_right_dir = self.scene.camera().right_xy();
self.inputs.look_dir =
Dir::from_unnormalized(cam_dir + aim_dir_offset).unwrap();
}
} }
self.inputs.strafing = matches!(
self.scene.camera().get_mode(), let (viewpoint_entity, mutable_viewpoint) = self.viewpoint_entity();
camera::CameraMode::FirstPerson
);
// Get the current state of movement related inputs // Get the current state of movement related inputs
let input_vec = self.key_state.dir_vec(); let input_vec = self.key_state.dir_vec();
let (axis_right, axis_up) = (input_vec[0], input_vec[1]); let (axis_right, axis_up) = (input_vec[0], input_vec[1]);
let dt = global_state.clock.get_stable_dt().as_secs_f32(); let dt = global_state.clock.get_stable_dt().as_secs_f32();
// Auto camera mode if mutable_viewpoint {
if global_state.settings.gameplay.auto_camera // If auto-gliding, point camera into the wind
&& matches!( if let Some(dir) = self
.auto_walk
.then_some(self.client.borrow())
.filter(|client| client.is_gliding())
.and_then(|client| {
let ecs = client.state().ecs();
let entity = client.entity();
let fluid = ecs
.read_storage::<comp::PhysicsState>()
.get(entity)?
.in_fluid?;
ecs.read_storage::<Vel>()
.get(entity)
.map(|vel| fluid.relative_flow(vel).0)
.map(|rel_flow| {
let is_wind_downwards =
rel_flow.dot(Vec3::unit_z()).is_sign_negative();
if !self.free_look {
if is_wind_downwards {
self.scene.camera().forward_xy().into()
} else {
let windwards = rel_flow
* self
.scene
.camera()
.forward_xy()
.dot(rel_flow.xy())
.signum();
Plane::from(Dir::new(self.scene.camera().right()))
.projection(windwards)
}
} else if is_wind_downwards {
Vec3::from(-rel_flow.xy())
} else {
-rel_flow
}
})
.and_then(Dir::from_unnormalized)
})
{
self.key_state.auto_walk = false;
self.inputs.move_dir = Vec2::zero();
self.inputs.look_dir = dir;
} else {
self.key_state.auto_walk = self.auto_walk;
if !self.free_look {
self.walk_forward_dir = self.scene.camera().forward_xy();
self.walk_right_dir = self.scene.camera().right_xy();
self.inputs.look_dir =
Dir::from_unnormalized(cam_dir + aim_dir_offset).unwrap();
}
}
self.inputs.strafing = matches!(
self.scene.camera().get_mode(), self.scene.camera().get_mode(),
camera::CameraMode::ThirdPerson | camera::CameraMode::FirstPerson camera::CameraMode::FirstPerson
) );
&& input_vec.magnitude_squared() > 0.0
{ // Auto camera mode
let camera = self.scene.camera_mut(); if global_state.settings.gameplay.auto_camera
let ori = camera.get_orientation(); && matches!(
camera.set_orientation_instant(Vec3::new( self.scene.camera().get_mode(),
ori.x camera::CameraMode::ThirdPerson | camera::CameraMode::FirstPerson
+ input_vec.x )
* (3.0 - input_vec.y * 1.5 * if is_aiming { 1.5 } else { 1.0 }) && input_vec.magnitude_squared() > 0.0
* dt, {
std::f32::consts::PI * if is_aiming { 0.015 } else { 0.1 }, let camera = self.scene.camera_mut();
0.0, let ori = camera.get_orientation();
)); camera.set_orientation_instant(Vec3::new(
ori.x
+ input_vec.x
* (3.0 - input_vec.y * 1.5 * if is_aiming { 1.5 } else { 1.0 })
* dt,
std::f32::consts::PI * if is_aiming { 0.015 } else { 0.1 },
0.0,
));
}
self.inputs.climb = self.key_state.climb();
self.inputs.move_z =
self.key_state.swim_up as i32 as f32 - self.key_state.swim_down as i32 as f32;
} }
match self.scene.camera().get_mode() { match self.scene.camera().get_mode() {
CameraMode::FirstPerson | CameraMode::ThirdPerson => { CameraMode::FirstPerson | CameraMode::ThirdPerson => {
// Move the player character based on their walking direction. if mutable_viewpoint {
// This could be different from the camera direction if free look is enabled. // Move the player character based on their walking direction.
self.inputs.move_dir = // This could be different from the camera direction if free look is
self.walk_right_dir * axis_right + self.walk_forward_dir * axis_up; // enabled.
self.inputs.move_dir =
self.walk_right_dir * axis_right + self.walk_forward_dir * axis_up;
}
self.freefly_vel = Vec3::zero(); self.freefly_vel = Vec3::zero();
}, },
CameraMode::Freefly => { CameraMode::Freefly => {
// Move the camera freely in 3d space. Apply acceleration so that // Move the camera freely in 3d space. Apply acceleration so that
// the movement feels more natural and controlled. // the movement feels more natural and controlled.
@ -1084,10 +1137,6 @@ impl PlayState for SessionState {
}, },
}; };
self.inputs.climb = self.key_state.climb();
self.inputs.move_z =
self.key_state.swim_up as i32 as f32 - self.key_state.swim_down as i32 as f32;
let mut outcomes = Vec::new(); let mut outcomes = Vec::new();
// Runs if either in a multiplayer server or the singleplayer server is unpaused // Runs if either in a multiplayer server or the singleplayer server is unpaused
@ -1177,6 +1226,8 @@ impl PlayState for SessionState {
self.scene.camera().get_mode(), self.scene.camera().get_mode(),
camera::CameraMode::FirstPerson camera::CameraMode::FirstPerson
), ),
viewpoint_entity,
mutable_viewpoint,
target_entity: self.target_entity, target_entity: self.target_entity,
selected_entity: self.selected_entity, selected_entity: self.selected_entity,
}, },
@ -1608,7 +1659,8 @@ impl PlayState for SessionState {
let scene_data = SceneData { let scene_data = SceneData {
client: &client, client: &client,
state: client.state(), state: client.state(),
player_entity: client.entity(), viewpoint_entity,
mutable_viewpoint: mutable_viewpoint || self.free_look,
// Only highlight if interactable // Only highlight if interactable
target_entity: self.interactable.and_then(Interactable::entity), target_entity: self.interactable.and_then(Interactable::entity),
loaded_distance: client.loaded_distance(), loaded_distance: client.loaded_distance(),
@ -1688,10 +1740,13 @@ impl PlayState for SessionState {
let client = self.client.borrow(); let client = self.client.borrow();
let (viewpoint_entity, mutable_viewpoint) = self.viewpoint_entity();
let scene_data = SceneData { let scene_data = SceneData {
client: &client, client: &client,
state: client.state(), state: client.state(),
player_entity: client.entity(), viewpoint_entity,
mutable_viewpoint,
// Only highlight if interactable // Only highlight if interactable
target_entity: self.interactable.and_then(Interactable::entity), target_entity: self.interactable.and_then(Interactable::entity),
loaded_distance: client.loaded_distance(), loaded_distance: client.loaded_distance(),

View File

@ -190,6 +190,7 @@ impl ControlSettings {
GameInput::MapZoomOut => KeyMouse::Key(VirtualKeyCode::Minus), GameInput::MapZoomOut => KeyMouse::Key(VirtualKeyCode::Minus),
GameInput::MapSetMarker => KeyMouse::Mouse(MouseButton::Middle), GameInput::MapSetMarker => KeyMouse::Mouse(MouseButton::Middle),
GameInput::SpectateSpeedBoost => KeyMouse::Key(VirtualKeyCode::LControl), GameInput::SpectateSpeedBoost => KeyMouse::Key(VirtualKeyCode::LControl),
GameInput::SpectateViewpoint => KeyMouse::Mouse(MouseButton::Middle),
} }
} }
} }

View File

@ -1,6 +1,5 @@
mod pixel_art; mod pixel_art;
mod renderer; mod renderer;
pub use renderer::{SampleStrat, Transform}; pub use renderer::{SampleStrat, Transform};
use crate::{ use crate::{

View File

@ -3,7 +3,7 @@ use crate::hud::{
get_quality_col, get_quality_col,
img_ids::Imgs, img_ids::Imgs,
item_imgs::{animate_by_pulse, ItemImgs}, item_imgs::{animate_by_pulse, ItemImgs},
util, util, HudInfo,
}; };
use client::Client; use client::Client;
use common::{ use common::{
@ -291,6 +291,7 @@ pub struct ItemTooltip<'a> {
transparency: f32, transparency: f32,
image_frame: ImageFrame, image_frame: ImageFrame,
client: &'a Client, client: &'a Client,
info: &'a HudInfo,
imgs: &'a Imgs, imgs: &'a Imgs,
item_imgs: &'a ItemImgs, item_imgs: &'a ItemImgs,
pulse: f32, pulse: f32,
@ -352,6 +353,7 @@ impl<'a> ItemTooltip<'a> {
pub fn new( pub fn new(
image_frame: ImageFrame, image_frame: ImageFrame,
client: &'a Client, client: &'a Client,
info: &'a HudInfo,
imgs: &'a Imgs, imgs: &'a Imgs,
item_imgs: &'a ItemImgs, item_imgs: &'a ItemImgs,
pulse: f32, pulse: f32,
@ -369,6 +371,7 @@ impl<'a> ItemTooltip<'a> {
image: None, image: None,
image_dims: None, image_dims: None,
client, client,
info,
imgs, imgs,
item_imgs, item_imgs,
pulse, pulse,
@ -448,7 +451,7 @@ impl<'a> Widget for ItemTooltip<'a> {
let i18n = &self.localized_strings; let i18n = &self.localized_strings;
let inventories = self.client.inventories(); let inventories = self.client.inventories();
let inventory = match inventories.get(self.client.entity()) { let inventory = match inventories.get(self.info.viewpoint_entity) {
Some(l) => l, Some(l) => l,
None => return, None => return,
}; };