Purged stats, including level and experience

This commit is contained in:
Sam 2021-01-01 17:39:36 -05:00
parent 82885af9c4
commit c0c45a1996
30 changed files with 107 additions and 774 deletions

View File

@ -4,7 +4,7 @@ ItemDef(
kind: Consumable(
kind: "PotionExp",
effect: [
Xp(250),
/*Xp(250),*/
]
),
quality: High,

View File

@ -4,7 +4,7 @@ ItemDef(
kind: Consumable(
kind: "Potion",
effect: [
Xp(250),
/*Xp(250),*/
]
),
quality: High,

View File

@ -4,7 +4,7 @@ ItemDef(
kind: Consumable(
kind: "Velorite",
effect: [
Xp(20),
/*Xp(20),*/
]
),
quality: High,

View File

@ -4,7 +4,7 @@ ItemDef(
kind: Consumable(
kind: "VeloriteFrag",
effect: [
Xp(10),
/*Xp(10),*/
]
),
quality: Moderate,

View File

@ -1732,7 +1732,6 @@ impl Client {
player_info.character = match &player_info.character {
Some(character) => Some(msg::CharacterInfo {
name: character.name.to_string(),
level: next_level,
}),
None => {
warn!(

View File

@ -157,7 +157,6 @@ pub struct PlayerInfo {
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CharacterInfo {
pub name: String,
pub level: u32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]

View File

@ -20,6 +20,5 @@ pub struct Character {
pub struct CharacterItem {
pub character: Character,
pub body: comp::Body,
pub level: usize,
pub inventory: Inventory,
}

View File

@ -46,7 +46,6 @@ pub enum ChatCommand {
Dummy,
Explosion,
Faction,
GiveExp,
GiveItem,
Goto,
Group,
@ -72,7 +71,6 @@ pub enum ChatCommand {
Region,
RemoveLights,
Say,
SetLevel,
SetMotd,
Spawn,
Sudo,
@ -99,7 +97,6 @@ pub static CHAT_COMMANDS: &[ChatCommand] = &[
ChatCommand::Dummy,
ChatCommand::Explosion,
ChatCommand::Faction,
ChatCommand::GiveExp,
ChatCommand::GiveItem,
ChatCommand::Goto,
ChatCommand::Group,
@ -125,7 +122,6 @@ pub static CHAT_COMMANDS: &[ChatCommand] = &[
ChatCommand::Region,
ChatCommand::RemoveLights,
ChatCommand::Say,
ChatCommand::SetLevel,
ChatCommand::SetMotd,
ChatCommand::Spawn,
ChatCommand::Sudo,
@ -244,11 +240,6 @@ impl ChatCommand {
"Send messages to your faction",
NoAdmin,
),
ChatCommand::GiveExp => cmd(
vec![Integer("amount", 50, Required)],
"Give experience to yourself",
Admin,
),
ChatCommand::GiveItem => cmd(
vec![
Enum("item", ITEM_SPECS.clone(), Required),
@ -378,11 +369,6 @@ impl ChatCommand {
"Send messages to everyone within shouting distance",
NoAdmin,
),
ChatCommand::SetLevel => cmd(
vec![Integer("level", 10, Required)],
"Set player Level",
Admin,
),
ChatCommand::SetMotd => {
cmd(vec![Message(Optional)], "Set the server description", Admin)
},
@ -452,7 +438,6 @@ impl ChatCommand {
ChatCommand::Dummy => "dummy",
ChatCommand::Explosion => "explosion",
ChatCommand::Faction => "faction",
ChatCommand::GiveExp => "give_exp",
ChatCommand::GiveItem => "give_item",
ChatCommand::Goto => "goto",
ChatCommand::Group => "group",
@ -478,7 +463,6 @@ impl ChatCommand {
ChatCommand::Region => "region",
ChatCommand::RemoveLights => "remove_lights",
ChatCommand::Say => "say",
ChatCommand::SetLevel => "set_level",
ChatCommand::SetMotd => "set_motd",
ChatCommand::Spawn => "spawn",
ChatCommand::Sudo => "sudo",

View File

@ -68,5 +68,5 @@ pub use player::Player;
pub use projectile::{Projectile, ProjectileConstructor};
pub use shockwave::{Shockwave, ShockwaveHitEntities};
pub use skills::{Skill, SkillGroup, SkillGroupType, SkillSet};
pub use stats::{Exp, Level, Stats};
pub use stats::Stats;
pub use visual::{LightAnimation, LightEmitter};

View File

@ -1,23 +1,12 @@
use crate::{
comp,
comp::{body::humanoid::Species, skills::SkillSet, Body},
comp::{skills::SkillSet, Body},
};
use serde::{Deserialize, Serialize};
use specs::{Component, DerefFlaggedStorage};
use specs_idvs::IdvStorage;
use std::{error::Error, fmt};
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
pub struct Exp {
current: u32,
maximum: u32,
}
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
pub struct Level {
amount: u32,
}
#[derive(Debug)]
pub enum StatChangeError {
Underflow,
@ -34,84 +23,18 @@ impl fmt::Display for StatChangeError {
}
impl Error for StatChangeError {}
impl Exp {
/// Used to determine how much exp is required to reach the next level. When
/// a character levels up, the next level target is increased by this value
const EXP_INCREASE_FACTOR: u32 = 25;
pub fn current(&self) -> u32 { self.current }
pub fn maximum(&self) -> u32 { self.maximum }
pub fn set_current(&mut self, current: u32) { self.current = current; }
pub fn change_by(&mut self, current: i64) {
self.current = ((self.current as i64) + current) as u32;
}
pub fn change_maximum_by(&mut self, maximum: i64) {
self.maximum = ((self.maximum as i64) + maximum) as u32;
}
pub fn update_maximum(&mut self, level: u32) {
self.maximum = level
.saturating_mul(Self::EXP_INCREASE_FACTOR)
.saturating_add(Self::EXP_INCREASE_FACTOR);
}
}
impl Level {
pub fn set_level(&mut self, level: u32) { self.amount = level; }
pub fn level(&self) -> u32 { self.amount }
pub fn change_by(&mut self, level: u32) { self.amount += level; }
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Stats {
pub name: String,
pub level: Level,
pub exp: Exp,
pub skill_set: SkillSet,
pub endurance: u32,
pub fitness: u32,
pub willpower: u32,
pub body_type: Body,
}
impl Stats {
pub fn new(name: String, body: Body) -> Self {
let species = if let comp::Body::Humanoid(hbody) = body {
Some(hbody.species)
} else {
None
};
// TODO: define base stats somewhere else (maybe method on Body?)
let (endurance, fitness, willpower) = match species {
Some(Species::Danari) => (0, 2, 3), // Small, flexible, intelligent, physically weak
Some(Species::Dwarf) => (2, 2, 1), // physically strong, intelligent, slow reflexes
Some(Species::Elf) => (1, 2, 2), // Intelligent, quick, physically weak
Some(Species::Human) => (2, 1, 2), // Perfectly balanced
Some(Species::Orc) => (3, 2, 0), /* Physically strong, non intelligent, medium */
// reflexes
Some(Species::Undead) => (1, 3, 1), /* Very good reflexes, equally intelligent and */
// strong
None => (0, 0, 0),
};
Self {
name,
level: Level { amount: 1 },
exp: Exp {
current: 0,
maximum: 50,
},
skill_set: SkillSet::default(),
endurance,
fitness,
willpower,
body_type: body,
}
}
@ -121,23 +44,10 @@ impl Stats {
pub fn empty() -> Self {
Self {
name: "".to_owned(),
level: Level { amount: 1 },
exp: Exp {
current: 0,
maximum: 50,
},
skill_set: SkillSet::default(),
endurance: 0,
fitness: 0,
willpower: 0,
body_type: comp::Body::Humanoid(comp::body::humanoid::Body::random()),
}
}
pub fn with_level(mut self, level: u32) -> Self {
self.level.set_level(level);
self
}
}
impl Component for Stats {

View File

@ -5,7 +5,6 @@ use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub enum Effect {
Health(comp::HealthChange),
Xp(i64),
Damage(combat::Damage),
Buff(BuffEffect),
}
@ -22,7 +21,6 @@ impl Effect {
pub fn info(&self) -> String {
match self {
Effect::Health(c) => format!("{:+} health", c.amount),
Effect::Xp(n) => format!("{:+} exp", n),
Effect::Damage(d) => format!("{:+}", d.value),
Effect::Buff(e) => format!("{:?} buff", e),
}
@ -33,9 +31,6 @@ impl Effect {
Effect::Health(change) => {
change.amount = (change.amount as f32 * modifier) as i32;
},
Effect::Xp(amount) => {
*amount = (*amount as f32 * modifier) as i64;
},
Effect::Damage(damage) => {
damage.interpolate_damage(modifier, 0.0);
},

View File

@ -86,7 +86,6 @@ fn get_handler(cmd: &ChatCommand) -> CommandHandler {
ChatCommand::Dummy => handle_spawn_training_dummy,
ChatCommand::Explosion => handle_explosion,
ChatCommand::Faction => handle_faction,
ChatCommand::GiveExp => handle_give_exp,
ChatCommand::GiveItem => handle_give_item,
ChatCommand::Goto => handle_goto,
ChatCommand::Group => handle_group,
@ -112,7 +111,6 @@ fn get_handler(cmd: &ChatCommand) -> CommandHandler {
ChatCommand::Region => handle_region,
ChatCommand::RemoveLights => handle_remove_lights,
ChatCommand::Say => handle_say,
ChatCommand::SetLevel => handle_set_level,
ChatCommand::SetMotd => handle_set_motd,
ChatCommand::Spawn => handle_spawn,
ChatCommand::Sudo => handle_sudo,
@ -927,10 +925,7 @@ fn handle_spawn_training_dummy(
let body = comp::Body::Object(comp::object::Body::TrainingDummy);
let mut stats = comp::Stats::new("Training Dummy".to_string(), body);
// Level 0 will prevent exp gain from kill
stats.level.set_level(0);
let stats = comp::Stats::new("Training Dummy".to_string(), body);
let health = comp::Health::new(body, 0);
@ -1019,11 +1014,10 @@ fn handle_players(
format!("{} online players:", entity_tuples.join().count()),
|s, (_, player, stat)| {
format!(
"{}\n[{}]{} Lvl {}",
"{}\n[{}]{}",
s,
player.alias,
stat.name,
stat.level.level()
)
},
),
@ -2000,130 +1994,6 @@ spawn_rate {:?} "#,
}
}
fn find_target(
ecs: &specs::World,
opt_alias: Option<String>,
fallback: EcsEntity,
) -> Result<EcsEntity, ServerGeneral> {
if let Some(alias) = opt_alias {
(&ecs.entities(), &ecs.read_storage::<comp::Player>())
.join()
.find(|(_, player)| player.alias == alias)
.map(|(entity, _)| entity)
.ok_or_else(|| {
ServerGeneral::server_msg(
ChatType::CommandError,
format!("Player '{}' not found!", alias),
)
})
} else {
Ok(fallback)
}
}
fn handle_give_exp(
server: &mut Server,
client: EcsEntity,
target: EcsEntity,
args: String,
action: &ChatCommand,
) {
let (a_exp, a_alias) = scan_fmt_some!(&args, &action.arg_fmt(), i64, String);
if let Some(exp) = a_exp {
let ecs = server.state.ecs_mut();
let target = find_target(&ecs, a_alias, target);
let mut error_msg = None;
match target {
Ok(player) => {
if let Some(mut stats) = ecs.write_storage::<comp::Stats>().get_mut(player) {
stats.exp.change_by(exp);
} else {
error_msg = Some(ServerGeneral::server_msg(
ChatType::CommandError,
"Player has no stats!",
));
}
},
Err(e) => {
error_msg = Some(e);
},
}
if let Some(msg) = error_msg {
server.notify_client(client, msg);
}
}
}
fn handle_set_level(
server: &mut Server,
client: EcsEntity,
target: EcsEntity,
args: String,
action: &ChatCommand,
) {
let (a_lvl, a_alias) = scan_fmt_some!(&args, &action.arg_fmt(), u32, String);
if let Some(lvl) = a_lvl {
let target = find_target(&server.state.ecs(), a_alias, target);
let mut error_msg = None;
match target {
Ok(player) => {
let uid = *server
.state
.ecs()
.read_storage::<Uid>()
.get(player)
.expect("Failed to get uid for player");
server.state.notify_players(ServerGeneral::PlayerListUpdate(
PlayerListUpdate::LevelChange(uid, lvl),
));
let body_type: Option<comp::Body>;
if let Some(mut stats) = server
.state
.ecs_mut()
.write_storage::<comp::Stats>()
.get_mut(player)
{
stats.level.set_level(lvl);
body_type = Some(stats.body_type);
} else {
error_msg = Some(ServerGeneral::server_msg(
ChatType::CommandError,
"Player has no stats!",
));
body_type = None;
}
if let Some(mut health) = server
.state
.ecs_mut()
.write_storage::<comp::Health>()
.get_mut(player)
{
let health = &mut *health;
health.update_max_hp(body_type, lvl);
health.set_to(health.maximum(), comp::HealthSource::LevelUp);
}
},
Err(e) => {
error_msg = Some(e);
},
}
if let Some(msg) = error_msg {
server.notify_client(client, msg);
}
}
}
fn handle_debug(
server: &mut Server,
client: EcsEntity,

View File

@ -189,8 +189,7 @@ pub fn handle_destroy(server: &mut Server, entity: EcsEntity, cause: HealthSourc
const MAX_EXP_DIST: f32 = 150.0;
// Attacker gets same as exp of everyone else
const ATTACKER_EXP_WEIGHT: f32 = 1.0;
let mut exp_reward = entity_stats.body_type.base_exp() as f32
* (1.0 + entity_stats.level.level() as f32 * 0.1);
let mut exp_reward = entity_stats.body_type.base_exp() as f32;
// Distribute EXP to group
let positions = state.ecs().read_storage::<Pos>();

View File

@ -1,4 +1 @@
-- Drops skill and skill_group tables
DROP TABLE skill;
DROP TABLE skill_group;
-- What's a down migration?

View File

@ -1,5 +1,37 @@
-- Creates skill and skill_group tables. Adds General skill tree for players that are already created
-- Creates new character table
CREATE TABLE "_character_new" (
"character_id" INT NOT NULL,
"player_uuid" TEXT NOT NULL,
"alias" TEXT NOT NULL,
"waypoint" TEXT NOT NULL,
PRIMARY KEY("character_id"),
FOREIGN KEY("character_id") REFERENCES "body"("body_id"),
FOREIGN KEY("character_id") REFERENCES "item"("item_id")
);
-- Inserts information into new character table
INSERT INTO _character_new
SELECT c.character_id,
c.player_uuid,
c.alias,
s.waypoint
FROM character c
JOIN stats s ON (s.stats_id = c.character_id);
-- Drops old character rable
PRAGMA foreign_keys = OFF;
DROP TABLE character;
ALTER TABLE _character_new RENAME TO character;
PRAGMA foreign_keys = ON;
-- Drops deprecated stats table
DROP TABLE stats;
-- Creates new table for skill groups
CREATE TABLE skill_group (
character_id INTEGER NOT NULL,
skill_group_type TEXT NOT NULL,
@ -9,6 +41,7 @@ CREATE TABLE skill_group (
PRIMARY KEY(character_id,skill_group_type)
);
-- Creates new table for skills
CREATE TABLE skill (
character_id INTEGER NOT NULL,
skill TEXT NOT NULL,
@ -17,6 +50,7 @@ CREATE TABLE skill (
PRIMARY KEY(character_id,skill)
);
-- Inserts starting skill group for everyone
INSERT INTO skill_group
SELECT c.character_id, '"General"', 0, 0
FROM character c

View File

@ -16,8 +16,8 @@ use crate::{
convert_character_from_database, convert_inventory_from_database_items,
convert_items_to_database_items, convert_loadout_from_database_items,
convert_skill_groups_to_database, convert_skills_to_database,
convert_stats_from_database, convert_stats_to_database,
convert_waypoint_from_database_json,
convert_stats_from_database, convert_waypoint_from_database_json,
convert_waypoint_to_database_json,
},
character_loader::{CharacterCreationResult, CharacterDataResult, CharacterListResult},
error::Error::DatabaseError,
@ -75,33 +75,30 @@ pub fn load_character_data(
.filter(parent_container_item_id.eq(character_containers.loadout_container_id))
.load::<Item>(&*connection)?;
let (character_data, stats_data) = character
let character_data = character
.filter(
schema::character::dsl::character_id
.eq(char_id)
.and(player_uuid.eq(requesting_player_uuid)),
)
.inner_join(schema::stats::dsl::stats)
.first::<(Character, Stats)>(&*connection)?;
.first::<Character>(&*connection)?;
let char_body = body
.filter(schema::body::dsl::body_id.eq(char_id))
.first::<Body>(&*connection)?;
let char_waypoint =
stats_data
.waypoint
.as_ref()
.and_then(|x| match convert_waypoint_from_database_json(&x) {
Ok(w) => Some(w),
Err(e) => {
warn!(
"Error reading waypoint from database for character ID {}, error: {}",
char_id, e
);
None
},
});
let char_waypoint = character_data.waypoint.as_ref().and_then(|x| {
match convert_waypoint_from_database_json(&x) {
Ok(w) => Some(w),
Err(e) => {
warn!(
"Error reading waypoint from database for character ID {}, error: {}",
char_id, e
);
None
},
}
});
let skill_data = schema::skill::dsl::skill
.filter(schema::skill::dsl::character_id.eq(char_id))
@ -113,12 +110,7 @@ pub fn load_character_data(
Ok((
convert_body_from_database(&char_body)?,
convert_stats_from_database(
&stats_data,
character_data.alias,
&skill_data,
&skill_group_data,
),
convert_stats_from_database(character_data.alias, &skill_data, &skill_group_data),
convert_inventory_from_database_items(&inventory_items, &loadout_items)?,
char_waypoint,
))
@ -135,17 +127,16 @@ pub fn load_character_list(
player_uuid_: &str,
connection: VelorenTransaction,
) -> CharacterListResult {
use schema::{body::dsl::*, character::dsl::*, item::dsl::*, stats::dsl::*};
use schema::{body::dsl::*, character::dsl::*, item::dsl::*};
let result = character
.filter(player_uuid.eq(player_uuid_))
.inner_join(stats)
.order(schema::character::dsl::character_id.desc())
.load::<(Character, Stats)>(&*connection)?;
.load::<Character>(&*connection)?;
result
.iter()
.map(|(character_data, char_stats)| {
.map(|character_data| {
let char = convert_character_from_database(character_data);
let db_body = body
@ -171,7 +162,6 @@ pub fn load_character_list(
Ok(CharacterItem {
character: char,
body: char_body,
level: char_stats.level as usize,
inventory: Inventory::new_with_loadout(loadout),
})
})
@ -234,20 +224,7 @@ pub fn create_character(
)));
}
let skill_set = stats.skill_set.clone();
// Insert stats record
let db_stats = convert_stats_to_database(character_id, &stats, &waypoint)?;
let stats_count = diesel::insert_into(schema::stats::table)
.values(&db_stats)
.execute(&*connection)?;
if stats_count != 1 {
return Err(Error::OtherError(format!(
"Error inserting into stats table for char_id {}",
character_id
)));
}
let skill_set = stats.skill_set;
// Insert body record
let new_body = Body {
@ -272,6 +249,7 @@ pub fn create_character(
character_id,
player_uuid: uuid,
alias: &character_alias,
waypoint: convert_waypoint_to_database_json(waypoint),
};
let character_count = diesel::insert_into(character::table)
.values(&new_character)
@ -335,9 +313,7 @@ pub fn delete_character(
char_id: CharacterId,
connection: VelorenTransaction,
) -> CharacterListResult {
use schema::{
body::dsl::*, character::dsl::*, skill::dsl::*, skill_group::dsl::*, stats::dsl::*,
};
use schema::{body::dsl::*, character::dsl::*, skill::dsl::*, skill_group::dsl::*};
// Load the character to delete - ensures that the requesting player
// owns the character
@ -371,16 +347,6 @@ pub fn delete_character(
)));
}
// Delete stats
let stats_count = diesel::delete(stats.filter(schema::stats::dsl::stats_id.eq(char_id)))
.execute(&*connection)?;
if stats_count != 1 {
return Err(Error::OtherError(format!(
"Error deleting from stats table for char_id {}",
char_id
)));
}
// Delete body
let body_count = diesel::delete(body.filter(schema::body::dsl::body_id.eq(char_id)))
.execute(&*connection)?;
@ -568,7 +534,7 @@ pub fn update(
char_waypoint: Option<comp::Waypoint>,
connection: VelorenTransaction,
) -> Result<Vec<Arc<common::comp::item::ItemId>>, Error> {
use super::schema::{item::dsl::*, skill_group::dsl::*};
use super::schema::{character::dsl::*, item::dsl::*, skill_group::dsl::*};
let pseudo_containers = get_pseudo_containers(connection, char_id)?;
@ -630,7 +596,7 @@ pub fn update(
}
}
let char_skill_set = char_stats.skill_set.clone();
let char_skill_set = char_stats.skill_set;
let db_skill_groups = convert_skill_groups_to_database(char_id, char_skill_set.skill_groups);
@ -644,15 +610,15 @@ pub fn update(
.values(&db_skills)
.execute(&*connection)?;
let db_stats = convert_stats_to_database(char_id, &char_stats, &char_waypoint)?;
let stats_count =
diesel::update(schema::stats::dsl::stats.filter(schema::stats::dsl::stats_id.eq(char_id)))
.set(db_stats)
let db_waypoint = convert_waypoint_to_database_json(char_waypoint);
let waypoint_count =
diesel::update(character.filter(schema::character::dsl::character_id.eq(char_id)))
.set(waypoint.eq(db_waypoint))
.execute(&*connection)?;
if stats_count != 1 {
if waypoint_count != 1 {
return Err(Error::OtherError(format!(
"Error updating stats table for char_id {}",
"Error updating character table for char_id {}",
char_id
)));
}

View File

@ -1,6 +1,6 @@
use crate::persistence::{
character::EntityId,
models::{Body, Character, Item, Skill, SkillGroup, Stats},
models::{Body, Character, Item, Skill, SkillGroup},
};
use crate::persistence::{
@ -164,12 +164,22 @@ pub fn convert_body_to_database_json(body: &CompBody) -> Result<String, Error> {
serde_json::to_string(&json_model).map_err(Error::SerializationError)
}
fn convert_waypoint_to_database_json(waypoint: &Waypoint) -> Result<String, Error> {
let charpos = CharacterPosition {
waypoint: waypoint.get_pos(),
};
serde_json::to_string(&charpos)
.map_err(|err| Error::ConversionError(format!("Error encoding waypoint: {:?}", err)))
pub fn convert_waypoint_to_database_json(waypoint: Option<Waypoint>) -> Option<String> {
match waypoint {
Some(w) => {
let charpos = CharacterPosition {
waypoint: w.get_pos(),
};
Some(
serde_json::to_string(&charpos)
.map_err(|err| {
Error::ConversionError(format!("Error encoding waypoint: {:?}", err))
})
.ok()?,
)
},
None => None,
}
}
pub fn convert_waypoint_from_database_json(position: &str) -> Result<Waypoint, Error> {
@ -183,27 +193,6 @@ pub fn convert_waypoint_from_database_json(position: &str) -> Result<Waypoint, E
Ok(Waypoint::new(character_position.waypoint, Time(0.0)))
}
pub fn convert_stats_to_database(
character_id: CharacterId,
stats: &common::comp::Stats,
waypoint: &Option<common::comp::Waypoint>,
) -> Result<Stats, Error> {
let waypoint = match waypoint {
Some(w) => Some(convert_waypoint_to_database_json(&w)?),
None => None,
};
Ok(Stats {
stats_id: character_id,
level: stats.level.level() as i32,
exp: stats.exp.current() as i32,
endurance: stats.endurance as i32,
fitness: stats.fitness as i32,
willpower: stats.willpower as i32,
waypoint,
})
}
pub fn convert_inventory_from_database_items(
inventory_items: &[Item],
loadout_items: &[Item],
@ -345,24 +334,17 @@ pub fn convert_character_from_database(character: &Character) -> common::charact
}
pub fn convert_stats_from_database(
stats: &Stats,
alias: String,
skills: &[Skill],
skill_groups: &[SkillGroup],
) -> common::comp::Stats {
let mut new_stats = common::comp::Stats::empty();
new_stats.name = alias;
new_stats.level.set_level(stats.level as u32);
new_stats.exp.update_maximum(stats.level as u32);
new_stats.exp.set_current(stats.exp as u32);
/*new_stats.update_max_hp(new_stats.body_type);
new_stats.health.set_to(
new_stats.health.maximum(),
common::comp::HealthSource::Revive,
);*/
new_stats.endurance = stats.endurance as u32;
new_stats.fitness = stats.fitness as u32;
new_stats.willpower = stats.willpower as u32;
new_stats.skill_set = skills::SkillSet {
skill_groups: convert_skill_groups_from_database(skill_groups),
skills: convert_skills_from_database(skills),

View File

@ -1,6 +1,6 @@
extern crate serde_json;
use super::schema::{body, character, entity, item, skill, skill_group, stats};
use super::schema::{body, character, entity, item, skill, skill_group};
#[derive(Debug, Insertable, PartialEq)]
#[table_name = "entity"]
@ -14,6 +14,7 @@ pub struct NewCharacter<'a> {
pub character_id: i64,
pub player_uuid: &'a str,
pub alias: &'a str,
pub waypoint: Option<String>,
}
#[derive(Identifiable, Queryable, Debug)]
@ -23,6 +24,7 @@ pub struct Character {
pub character_id: i64,
pub player_uuid: String,
pub alias: String,
pub waypoint: Option<String>,
}
#[primary_key(item_id)]
@ -36,19 +38,6 @@ pub struct Item {
pub position: String,
}
#[derive(Associations, AsChangeset, Identifiable, Queryable, Debug, Insertable)]
#[primary_key(stats_id)]
#[table_name = "stats"]
pub struct Stats {
pub stats_id: i64,
pub level: i32,
pub exp: i32,
pub endurance: i32,
pub fitness: i32,
pub willpower: i32,
pub waypoint: Option<String>,
}
#[derive(Associations, Identifiable, Insertable, Queryable, Debug)]
#[primary_key(body_id)]
#[table_name = "body"]

View File

@ -11,6 +11,7 @@ table! {
character_id -> BigInt,
player_uuid -> Text,
alias -> Text,
waypoint -> Nullable<Text>,
}
}
@ -30,18 +31,6 @@ table! {
}
}
table! {
stats (stats_id) {
stats_id -> BigInt,
level -> Integer,
exp -> Integer,
endurance -> Integer,
fitness -> Integer,
willpower -> Integer,
waypoint -> Nullable<Text>,
}
}
table! {
skill (character_id, skill_type) {
character_id -> BigInt,
@ -61,6 +50,5 @@ table! {
}
joinable!(character -> body (character_id));
joinable!(character -> stats (character_id));
allow_tables_to_appear_in_same_query!(body, character, entity, item, stats,);
allow_tables_to_appear_in_same_query!(body, character, entity, item);

View File

@ -97,7 +97,7 @@ impl<'a> System<'a> for Sys {
let body = entity.get_body();
server_emitter.emit(ServerEvent::CreateNpc {
pos: comp::Pos(spawn_pos),
stats: comp::Stats::new(entity.get_name(), body).with_level(entity.get_level()),
stats: comp::Stats::new(entity.get_name(), body),
health: comp::Health::new(body, 10),
loadout: match body {
comp::Body::Humanoid(_) => entity.get_loadout(),

View File

@ -84,12 +84,6 @@ impl StateExt for State {
.get_mut(entity)
.map(|mut health| health.change_by(change));
},
Effect::Xp(xp) => {
self.ecs()
.write_storage::<comp::Stats>()
.get_mut(entity)
.map(|mut stats| stats.exp.change_by(xp));
},
Effect::Damage(damage) => {
let inventories = self.ecs().read_storage::<Inventory>();
let change = damage.modify_damage(inventories.get(entity), source);
@ -266,7 +260,6 @@ impl StateExt for State {
self.notify_players(ServerGeneral::PlayerListUpdate(
PlayerListUpdate::SelectedCharacter(player_uid, CharacterInfo {
name: String::from(&stats.name),
level: stats.level.level(),
}),
));
@ -278,7 +271,7 @@ impl StateExt for State {
self.write_component(entity, body);
self.write_component(
entity,
comp::Health::new(stats.body_type, stats.level.level()),
comp::Health::new(stats.body_type, 0), //Placeholder 0
);
self.write_component(entity, stats);
self.write_component(entity, inventory);

View File

@ -122,7 +122,6 @@ impl<'a> System<'a> for Sys {
player_alias: player.alias.clone(),
character: stats.map(|stats| CharacterInfo {
name: stats.name.clone(),
level: stats.level.level(),
}),
})
})

View File

@ -13,7 +13,6 @@ use common::{
};
use common_net::msg::ServerGeneral;
use common_sys::state::TerrainChanges;
use rand::Rng;
use specs::{Join, Read, ReadStorage, System, Write, WriteExpect};
use std::sync::Arc;
use vek::*;
@ -131,14 +130,6 @@ impl<'a> System<'a> for Sys {
let mut scale = entity.scale;
// TODO: Remove this and implement scaling or level depending on stuff like
// species instead
stats.level.set_level(
entity.level.unwrap_or_else(|| {
(rand::thread_rng().gen_range(1, 9) as f32 * scale) as u32
}),
);
// Replace stuff if it's a boss
if entity.is_giant {
if rand::random::<f32>() < 0.65 && entity.alignment != Alignment::Enemy {
@ -154,7 +145,6 @@ impl<'a> System<'a> for Sys {
body,
);
}
stats.level.set_level(rand::thread_rng().gen_range(25, 30));
scale = 2.0 + rand::random::<f32>();
}
@ -162,7 +152,7 @@ impl<'a> System<'a> for Sys {
let loadout = LoadoutBuilder::build_loadout(body, main_tool, config).build();
let health = comp::Health::new(stats.body_type, stats.level.level());
let health = comp::Health::new(stats.body_type, 0); // Placeholder 0
let can_speak = match body {
comp::Body::Humanoid(_) => alignment == comp::Alignment::Npc,

View File

@ -8,25 +8,9 @@ use specs::{Entity, World, WorldExt};
#[derive(Copy, Clone, Debug)]
pub struct MyEntity(pub Entity);
#[derive(Copy, Clone, Debug)]
pub struct ExpFloater {
pub exp_change: i32, // Maybe you can loose exp :p
pub timer: f32,
// Used to randomly offset position
pub rand: (f32, f32),
}
#[derive(Clone, Debug, Default)]
pub struct MyExpFloaterList {
pub floaters: Vec<ExpFloater>,
pub last_exp: u32,
pub last_level: u32,
pub last_exp_max: u32,
}
pub fn init(world: &mut World) {
world.register::<comp::HpFloaterList>();
world.register::<comp::Interpolated>();
world.insert(MyExpFloaterList::default());
world.insert(Vec::<Outcome>::new());
// Voxygen event buses

View File

@ -1,18 +1,17 @@
use crate::ecs::{
comp::{HpFloater, HpFloaterList},
ExpFloater, MyEntity, MyExpFloaterList,
MyEntity,
};
use common::{
comp::{Health, HealthSource, Pos, Stats},
comp::{Health, HealthSource, Pos},
resources::DeltaTime,
uid::Uid,
};
use specs::{Entities, Join, Read, ReadExpect, ReadStorage, System, Write, WriteStorage};
use specs::{Entities, Join, Read, ReadExpect, ReadStorage, System, WriteStorage};
// How long floaters last (in seconds)
pub const HP_SHOWTIME: f32 = 3.0;
pub const MY_HP_SHOWTIME: f32 = 2.5;
pub const MY_EXP_SHOWTIME: f32 = 4.0;
pub struct Sys;
impl<'a> System<'a> for Sys {
@ -21,10 +20,8 @@ impl<'a> System<'a> for Sys {
Entities<'a>,
ReadExpect<'a, MyEntity>,
Read<'a, DeltaTime>,
Write<'a, MyExpFloaterList>,
ReadStorage<'a, Uid>,
ReadStorage<'a, Pos>,
ReadStorage<'a, Stats>,
ReadStorage<'a, Health>,
WriteStorage<'a, HpFloaterList>,
);
@ -32,17 +29,7 @@ impl<'a> System<'a> for Sys {
#[allow(clippy::blocks_in_if_conditions)] // TODO: Pending review in #587
fn run(
&mut self,
(
entities,
my_entity,
dt,
mut my_exp_floater_list,
uids,
pos,
stats,
healths,
mut hp_floater_lists,
): Self::SystemData,
(entities, my_entity, dt, uids, pos, healths, mut hp_floater_lists): Self::SystemData,
) {
// Add hp floater lists to all entities with health and a position
// Note: necessary in order to know last_hp
@ -149,57 +136,5 @@ impl<'a> System<'a> for Sys {
floaters.clear();
}
}
// Update MyExpFloaterList
if let Some(stats) = stats.get(my_entity.0) {
let mut fl = my_exp_floater_list;
// Add a floater if exp changed
// TODO: can't handle if you level up more than once (maybe store total exp in
// stats)
let exp_change = if stats.level.level() != fl.last_level {
if stats.level.level() > fl.last_level {
stats.exp.current() as i32 + fl.last_exp_max as i32 - fl.last_exp as i32
} else {
// Level down
stats.exp.current() as i32 - stats.exp.maximum() as i32 - fl.last_exp as i32
}
} else {
stats.exp.current() as i32 - fl.last_exp as i32
};
if exp_change != 0 {
fl.floaters.push(ExpFloater {
timer: 0.0,
exp_change,
rand: (rand::random(), rand::random()),
});
}
// Increment timers
for mut floater in &mut fl.floaters {
floater.timer += dt.0;
}
// Clear if the newest is past show time
if fl
.floaters
.last()
.map_or(false, |f| f.timer > MY_EXP_SHOWTIME)
{
fl.floaters.clear();
}
// Update stored values
fl.last_exp = stats.exp.current();
fl.last_exp_max = stats.exp.maximum();
fl.last_level = stats.level.level();
} else {
// Clear if stats component doesn't exist
my_exp_floater_list.floaters.clear();
// Clear stored values
my_exp_floater_list.last_exp = 0;
my_exp_floater_list.last_exp_max = 0;
my_exp_floater_list.last_level = 0;
}
}
}

View File

@ -84,7 +84,6 @@ use std::{
};
use vek::*;
const XP_COLOR: Color = Color::Rgba(0.59, 0.41, 0.67, 1.0);
const TEXT_COLOR: Color = Color::Rgba(1.0, 1.0, 1.0, 1.0);
const TEXT_GRAY_COLOR: Color = Color::Rgba(0.5, 0.5, 0.5, 1.0);
const TEXT_DULL_RED_COLOR: Color = Color::Rgba(0.56, 0.2, 0.2, 1.0);
@ -825,9 +824,6 @@ impl Hud {
let items = ecs.read_storage::<comp::Item>();
let entities = ecs.entities();
let me = client.entity();
let own_level = stats
.get(client.entity())
.map_or(0, |stats| stats.level.level());
//self.input = client.read_storage::<comp::ControllerInputs>();
if let Some(health) = healths.get(me) {
// Hurt Frame
@ -1061,111 +1057,6 @@ impl Hud {
}
}
}
// EXP Numbers
if let (Some(floaters), Some(stats)) = (
Some(&*ecs.read_resource::<crate::ecs::MyExpFloaterList>())
.map(|l| &l.floaters)
.filter(|f| !f.is_empty()),
stats.get(me),
) {
// TODO replace with setting
let batched_sct = false;
if batched_sct {
let number_speed = 50.0; // Number Speed for Cumulated EXP
let player_sct_bg_id = player_sct_bg_id_walker.next(
&mut self.ids.player_sct_bgs,
&mut ui_widgets.widget_id_generator(),
);
let player_sct_id = player_sct_id_walker.next(
&mut self.ids.player_scts,
&mut ui_widgets.widget_id_generator(),
);
// Sum xp change
let exp_change = floaters.iter().fold(0, |acc, f| f.exp_change + acc);
// Can't fail since we filtered out empty lists above
let (timer, rand) = floaters
.last()
.map(|f| (f.timer, f.rand))
.expect("Impossible");
// Increase font size based on fraction of maximum health
// "flashes" by having a larger size in the first 100ms
let font_size_xp = 30
+ ((exp_change.abs() as f32 / stats.exp.maximum() as f32).min(1.0)
* 50.0) as u32
+ if timer < 0.1 {
FLASH_MAX * (((1.0 - timer / 0.1) * 10.0) as u32)
} else {
0
};
let y = timer as f64 * number_speed; // Timer sets the widget offset
let fade = ((4.0 - timer as f32) * 0.25) + 0.2; // Timer sets text transparency
Text::new(&format!("{} Exp", exp_change))
.font_size(font_size_xp)
.font_id(self.fonts.cyri.conrod_id)
.color(Color::Rgba(0.0, 0.0, 0.0, fade))
.x_y(
ui_widgets.win_w * (0.5 * rand.0 as f64 - 0.25),
ui_widgets.win_h * (0.15 * rand.1 as f64) + y - 3.0,
)
.set(player_sct_bg_id, ui_widgets);
Text::new(&format!("{} Exp", exp_change))
.font_size(font_size_xp)
.font_id(self.fonts.cyri.conrod_id)
.color(Color::Rgba(0.59, 0.41, 0.67, fade))
.x_y(
ui_widgets.win_w * (0.5 * rand.0 as f64 - 0.25),
ui_widgets.win_h * (0.15 * rand.1 as f64) + y,
)
.set(player_sct_id, ui_widgets);
} else {
for floater in floaters {
let number_speed = 50.0; // Number Speed for Single EXP
let player_sct_bg_id = player_sct_bg_id_walker.next(
&mut self.ids.player_sct_bgs,
&mut ui_widgets.widget_id_generator(),
);
let player_sct_id = player_sct_id_walker.next(
&mut self.ids.player_scts,
&mut ui_widgets.widget_id_generator(),
);
// Increase font size based on fraction of maximum health
// "flashes" by having a larger size in the first 100ms
let font_size_xp = 30
+ ((floater.exp_change.abs() as f32 / stats.exp.maximum() as f32)
.min(1.0)
* 50.0) as u32
+ if floater.timer < 0.1 {
FLASH_MAX * (((1.0 - floater.timer / 0.1) * 10.0) as u32)
} else {
0
};
let y = floater.timer as f64 * number_speed; // Timer sets the widget offset
let fade = ((4.0 - floater.timer as f32) * 0.25) + 0.2; // Timer sets text transparency
Text::new(&format!("{} Exp", floater.exp_change))
.font_size(font_size_xp)
.font_id(self.fonts.cyri.conrod_id)
.color(Color::Rgba(0.0, 0.0, 0.0, fade))
.x_y(
ui_widgets.win_w * (0.5 * floater.rand.0 as f64 - 0.25),
ui_widgets.win_h * (0.15 * floater.rand.1 as f64) + y - 3.0,
)
.set(player_sct_bg_id, ui_widgets);
Text::new(&format!("{} Exp", floater.exp_change))
.font_size(font_size_xp)
.font_id(self.fonts.cyri.conrod_id)
.color(Color::Rgba(0.59, 0.41, 0.67, fade))
.x_y(
ui_widgets.win_w * (0.5 * floater.rand.0 as f64 - 0.25),
ui_widgets.win_h * (0.15 * floater.rand.1 as f64) + y,
)
.set(player_sct_id, ui_widgets);
}
}
}
}
// Pop speech bubbles
@ -1273,7 +1164,6 @@ impl Hud {
let info = display_overhead_info.then(|| overhead::Info {
name: &stats.name,
stats,
health,
buffs,
energy,
@ -1313,7 +1203,6 @@ impl Hud {
overhead::Overhead::new(
info,
bubble,
own_level,
in_group,
&global_state.settings.gameplay,
self.pulse,
@ -1974,7 +1863,6 @@ impl Hud {
// Get player stats
let ecs = client.state().ecs();
let entity = client.entity();
let stats = ecs.read_storage::<comp::Stats>();
let healths = ecs.read_storage::<comp::Health>();
let inventories = ecs.read_storage::<comp::Inventory>();
let energies = ecs.read_storage::<comp::Energy>();
@ -1983,14 +1871,12 @@ impl Hud {
let ability_map = ecs.fetch::<comp::item::tool::AbilityMap>();
if let (
Some(stats),
Some(health),
Some(inventory),
Some(energy),
Some(_character_state),
Some(_controller),
) = (
stats.get(entity),
healths.get(entity),
inventories.get(entity),
energies.get(entity),
@ -2003,7 +1889,6 @@ impl Hud {
&self.item_imgs,
&self.fonts,
&self.rot_imgs,
&stats,
&health,
&inventory,
&energy,
@ -2014,7 +1899,6 @@ impl Hud {
tooltip_manager,
&mut self.slot_manager,
i18n,
&self.show,
&ability_map,
)
.set(self.ids.skillbar, ui_widgets);

View File

@ -8,7 +8,7 @@ use crate::{
settings::GameplaySettings,
ui::{fonts::Fonts, Ingameable},
};
use common::comp::{BuffKind, Buffs, Energy, Health, SpeechBubble, SpeechBubbleType, Stats};
use common::comp::{BuffKind, Buffs, Energy, Health, SpeechBubble, SpeechBubbleType};
use conrod_core::{
color,
position::Align,
@ -57,7 +57,6 @@ widget_ids! {
#[derive(Clone, Copy)]
pub struct Info<'a> {
pub name: &'a str,
pub stats: &'a Stats,
pub health: &'a Health,
pub buffs: &'a Buffs,
pub energy: Option<&'a Energy>,
@ -72,7 +71,6 @@ pub fn should_show_healthbar(health: &Health) -> bool { health.current() != heal
pub struct Overhead<'a> {
info: Option<Info<'a>>,
bubble: Option<&'a SpeechBubble>,
own_level: u32,
in_group: bool,
settings: &'a GameplaySettings,
pulse: f32,
@ -89,7 +87,6 @@ impl<'a> Overhead<'a> {
pub fn new(
info: Option<Info<'a>>,
bubble: Option<&'a SpeechBubble>,
own_level: u32,
in_group: bool,
settings: &'a GameplaySettings,
pulse: f32,
@ -100,7 +97,6 @@ impl<'a> Overhead<'a> {
Self {
info,
bubble,
own_level,
in_group,
settings,
pulse,
@ -171,7 +167,6 @@ impl<'a> Widget for Overhead<'a> {
const MANA_BAR_Y: f64 = MANA_BAR_HEIGHT / 2.0;
if let Some(Info {
name,
stats,
health,
buffs,
energy,
@ -180,13 +175,10 @@ impl<'a> Widget for Overhead<'a> {
// Used to set healthbar colours based on hp_percentage
let hp_percentage = health.current() as f64 / health.maximum() as f64 * 100.0;
// Compare levels to decide if a skull is shown
let level_comp = stats.level.level() as i64 - self.own_level as i64;
let health_current = (health.current() / 10) as f64;
let health_max = (health.maximum() / 10) as f64;
let name_y = if (health_current - health_max).abs() < 1e-6 {
MANA_BAR_Y + 20.0
} else if level_comp > 9 && !self.in_group {
MANA_BAR_Y + 38.0
} else {
MANA_BAR_Y + 32.0
};
@ -373,51 +365,6 @@ impl<'a> Widget for Overhead<'a> {
.color(Some(Color::Rgba(1.0, 1.0, 1.0, 0.99)))
.parent(id)
.set(state.ids.health_bar_fg, ui);
// Level
const LOW: Color = Color::Rgba(0.54, 0.81, 0.94, 0.4);
const HIGH: Color = Color::Rgba(1.0, 0.0, 0.0, 1.0);
const EQUAL: Color = Color::Rgba(1.0, 1.0, 1.0, 1.0);
// Change visuals of the level display depending on the player level/opponent
// level
let level_comp = stats.level.level() as i64 - self.own_level as i64;
// + 10 level above player -> skull
// + 5-10 levels above player -> high
// -5 - +5 levels around player level -> equal
// - 5 levels below player -> low
if level_comp > 9 && !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 {
let fnt_size = match stats.level.level() {
0..=9 => 15,
10..=99 => 12,
100..=999 => 9,
_ => 2,
};
Text::new(&format!("{}", stats.level.level()))
.font_id(self.fonts.cyri.conrod_id)
.font_size(fnt_size)
.color(if level_comp > 4 {
HIGH
} else if level_comp < -5 {
LOW
} else {
EQUAL
})
.x_y(-37.0 * BARSIZE, MANA_BAR_Y + 9.0)
.parent(id)
.set(state.ids.level, ui);
}
}
}

View File

@ -2,7 +2,7 @@ use super::{
hotbar,
img_ids::{Imgs, ImgsRot},
item_imgs::ItemImgs,
slots, BarNumbers, ShortcutNumbers, Show, BLACK, CRITICAL_HP_COLOR, HP_COLOR, LOW_HP_COLOR,
slots, BarNumbers, ShortcutNumbers, BLACK, CRITICAL_HP_COLOR, HP_COLOR, LOW_HP_COLOR,
STAMINA_COLOR, TEXT_COLOR, UI_HIGHLIGHT_0,
};
use crate::{
@ -21,7 +21,7 @@ use common::comp::{
tool::{AbilityMap, Tool, ToolKind},
Hands, ItemKind,
},
Energy, Health, Inventory, Stats,
Energy, Health, Inventory,
};
use conrod_core::{
color,
@ -29,7 +29,6 @@ use conrod_core::{
widget_ids, Color, Colorable, Positionable, Sizeable, Widget, WidgetCommon,
};
use inline_tweak::*;
use std::time::{Duration, Instant};
use vek::*;
widget_ids! {
@ -129,7 +128,6 @@ pub struct Skillbar<'a> {
item_imgs: &'a ItemImgs,
fonts: &'a Fonts,
rot_imgs: &'a ImgsRot,
stats: &'a Stats,
health: &'a Health,
inventory: &'a Inventory,
energy: &'a Energy,
@ -142,7 +140,6 @@ pub struct Skillbar<'a> {
pulse: f32,
#[conrod(common_builder)]
common: widget::CommonBuilder,
show: &'a Show,
ability_map: &'a AbilityMap,
}
@ -154,7 +151,6 @@ impl<'a> Skillbar<'a> {
item_imgs: &'a ItemImgs,
fonts: &'a Fonts,
rot_imgs: &'a ImgsRot,
stats: &'a Stats,
health: &'a Health,
inventory: &'a Inventory,
energy: &'a Energy,
@ -165,7 +161,6 @@ impl<'a> Skillbar<'a> {
tooltip_manager: &'a mut TooltipManager,
slot_manager: &'a mut slots::SlotManager,
localized_strings: &'a Localization,
show: &'a Show,
ability_map: &'a AbilityMap,
) -> Self {
Self {
@ -174,7 +169,6 @@ impl<'a> Skillbar<'a> {
item_imgs,
fonts,
rot_imgs,
stats,
health,
inventory,
energy,
@ -186,7 +180,6 @@ impl<'a> Skillbar<'a> {
tooltip_manager,
slot_manager,
localized_strings,
show,
ability_map,
}
}
@ -194,8 +187,6 @@ impl<'a> Skillbar<'a> {
pub struct State {
ids: Ids,
last_level: u32,
last_update_level: Instant,
}
impl<'a> Widget for Skillbar<'a> {
@ -206,8 +197,6 @@ impl<'a> Widget for Skillbar<'a> {
fn init_state(&self, id_gen: widget::id::Generator) -> Self::State {
State {
ids: Ids::new(id_gen),
last_level: 1,
last_update_level: Instant::now(),
}
}
@ -217,14 +206,6 @@ impl<'a> Widget for Skillbar<'a> {
fn update(self, args: widget::UpdateArgs<Self>) -> Self::Event {
let widget::UpdateArgs { state, ui, .. } = args;
let _level = if self.stats.level.level() > 999 {
"A".to_string()
} else {
(self.stats.level.level()).to_string()
};
let _exp_percentage = (self.stats.exp.current() as f64) / (self.stats.exp.maximum() as f64);
let mut hp_percentage = self.health.current() as f64 / self.health.maximum() as f64 * 100.0;
let mut energy_percentage =
self.energy.current() as f64 / self.energy.maximum() as f64 * 100.0;
@ -243,65 +224,6 @@ impl<'a> Widget for Skillbar<'a> {
let slot_offset = tweak!(3.0);
// Level Up Message
if !self.show.intro {
let current_level = self.stats.level.level();
const FADE_IN_LVL: f32 = 1.0;
const FADE_HOLD_LVL: f32 = 3.0;
const FADE_OUT_LVL: f32 = 2.0;
// Fade
// Check if no other popup is displayed and a new one is needed
if state.last_update_level.elapsed()
> Duration::from_secs_f32(FADE_IN_LVL + FADE_HOLD_LVL + FADE_OUT_LVL)
&& state.last_level != current_level
{
// Update last_value
state.update(|s| s.last_level = current_level);
state.update(|s| s.last_update_level = Instant::now());
};
let seconds_level = state.last_update_level.elapsed().as_secs_f32();
let fade_level = if current_level == 1 {
0.0
} else if seconds_level < FADE_IN_LVL {
seconds_level / FADE_IN_LVL
} else if seconds_level < FADE_IN_LVL + FADE_HOLD_LVL {
1.0
} else {
(1.0 - (seconds_level - FADE_IN_LVL - FADE_HOLD_LVL) / FADE_OUT_LVL).max(0.0)
};
// Contents
Rectangle::fill_with([82.0 * 4.0, 40.0 * 4.0], color::TRANSPARENT)
.mid_top_with_margin_on(ui.window, 300.0)
.set(state.ids.level_align, ui);
let level_up_text = &localized_strings
.get("char_selection.level_fmt")
.replace("{level_nb}", &self.stats.level.level().to_string());
Text::new(&level_up_text)
.middle_of(state.ids.level_align)
.font_size(self.fonts.cyri.scale(30))
.font_id(self.fonts.cyri.conrod_id)
.color(Color::Rgba(0.0, 0.0, 0.0, fade_level))
.set(state.ids.level_message_bg, ui);
Text::new(&level_up_text)
.bottom_left_with_margins_on(state.ids.level_message_bg, 2.0, 2.0)
.font_size(self.fonts.cyri.scale(30))
.font_id(self.fonts.cyri.conrod_id)
.color(Color::Rgba(1.0, 1.0, 1.0, fade_level))
.set(state.ids.level_message, ui);
Image::new(self.imgs.level_up)
.w_h(82.0 * 4.0, 9.0 * 4.0)
.mid_top_with_margin_on(state.ids.level_align, 0.0)
.color(Some(Color::Rgba(1.0, 1.0, 1.0, fade_level)))
.graphics_for(state.ids.level_align)
.set(state.ids.level_up, ui);
Image::new(self.imgs.level_down)
.w_h(82.0 * 4.0, 9.0 * 4.0)
.mid_bottom_with_margin_on(state.ids.level_align, 0.0)
.color(Some(Color::Rgba(1.0, 1.0, 1.0, fade_level)))
.graphics_for(state.ids.level_align)
.set(state.ids.level_down, ui);
}
// Death message
if self.health.is_dead {
if let Some(key) = self

View File

@ -401,16 +401,6 @@ impl<'a> Widget for Social<'a> {
},
None => alias.clone(), // character select or spectating
};
let level = match &player_info.character {
Some(character) => {
if character.level > 999 {
"[A]".to_string() // Hide player levels that can't be obtained by normal means. As "infinte" levels are temporary this will avoid clipping.
} else {
character.level.to_string()
}
},
None => "".to_string(), // character select or spectating
};
let zone_name = match &player_info.character {
None => self.localized_strings.get("hud.group.in_menu").to_string(), /* character select or spectating */
_ => format!("{} ", &zone),
@ -456,21 +446,6 @@ impl<'a> Widget for Social<'a> {
TEXT_COLOR,
)
.set(state.ids.player_names[i], ui);
// Player Levels
Button::image(if !selected {
self.imgs.nothing
} else {
self.imgs.selection
})
.w_h(39.0, 20.0)
.right_from(state.ids.player_names[i], 2.0)
.label(&level)
.label_font_size(self.fonts.cyri.scale(14))
.label_font_id(self.fonts.cyri.conrod_id)
.label_color(TEXT_COLOR)
.label_y(conrod_core::position::Relative::Scalar(1.0))
.parent(state.ids.levels_align)
.set(state.ids.player_levels[i], ui);
// Player Zones
Button::image(if !selected {
self.imgs.nothing

View File

@ -447,13 +447,6 @@ impl Controls {
// TODO: only construct string once when characters
// are
// loaded
Text::new(
i18n.get("char_selection.level_fmt").replace(
"{level_nb}",
&character.level.to_string(),
),
)
.into(),
Text::new(
i18n.get("char_selection.uncanny_valley"),
)