Started moving loading of ability manifest to an ecs variable.

This commit is contained in:
Sam 2020-11-11 23:00:17 -06:00
parent 050ad82e55
commit 9b4fa4e961
24 changed files with 133 additions and 104 deletions

View File

@ -1,7 +1,7 @@
use crate::{
assets::{self, Asset},
comp::{
item::{armor::Protection, Item, ItemKind},
item::{armor::Protection, tool::AbilityMap, Item, ItemKind},
projectile::ProjectileConstructor,
Body, CharacterState, EnergySource, Gravity, LightEmitter, StateUpdate,
},
@ -325,10 +325,10 @@ pub struct ItemConfig {
pub dodge_ability: Option<CharacterAbility>,
}
impl From<Item> for ItemConfig {
fn from(item: Item) -> Self {
impl From<(Item, &AbilityMap)> for ItemConfig {
fn from((item, map): (Item, &AbilityMap)) -> Self {
if let ItemKind::Tool(tool) = &item.kind() {
let abilities = tool.get_abilities().clone();
let abilities = tool.get_abilities(map).clone();
return ItemConfig {
item,

View File

@ -2,7 +2,7 @@ pub mod armor;
pub mod tool;
// Reexports
pub use tool::{Hands, Tool, ToolKind, UniqueKind};
pub use tool::{AbilitySet, Hands, Tool, ToolKind, UniqueKind};
use crate::{
assets::{self, Asset, Error},

View File

@ -95,7 +95,7 @@ impl Tool {
Duration::from_millis(millis).div_f32(self.base_speed())
}
pub fn get_abilities(&self) -> AbilitySet<CharacterAbility> {
pub fn get_abilities(&self, map: &AbilityMap) -> AbilitySet<CharacterAbility> {
let base_abilities = match AbilityMap::load("common.abilities.weapon_ability_manifest") {
Ok(map) => map.0.get(&self.kind).map(|a| a.clone()).unwrap_or_default(),
Err(err) => {

View File

@ -1,6 +1,6 @@
use crate::{
comp,
comp::{item, item::armor, ItemConfig},
comp::{item::{self, armor, tool::AbilityMap}, ItemConfig},
};
use comp::{Inventory, Loadout};
use serde::{Deserialize, Serialize};
@ -90,6 +90,7 @@ fn loadout_replace(
equip_slot: EquipSlot,
item: Option<item::Item>,
loadout: &mut Loadout,
map: &AbilityMap,
) -> Option<item::Item> {
use std::mem::replace;
match equip_slot {
@ -107,10 +108,10 @@ fn loadout_replace(
EquipSlot::Lantern => replace(&mut loadout.lantern, item),
EquipSlot::Glider => replace(&mut loadout.glider, item),
EquipSlot::Mainhand => {
replace(&mut loadout.active_item, item.map(ItemConfig::from)).map(|i| i.item)
replace(&mut loadout.active_item, item.map(|item| ItemConfig::from((item, map)))).map(|i| i.item)
},
EquipSlot::Offhand => {
replace(&mut loadout.second_item, item.map(ItemConfig::from)).map(|i| i.item)
replace(&mut loadout.second_item, item.map(|item| ItemConfig::from((item, map)))).map(|i| i.item)
},
}
}
@ -122,8 +123,9 @@ fn loadout_insert(
equip_slot: EquipSlot,
item: item::Item,
loadout: &mut Loadout,
map: &AbilityMap,
) -> Option<item::Item> {
loadout_replace(equip_slot, Some(item), loadout)
loadout_replace(equip_slot, Some(item), loadout, map)
}
/// Remove an item from a loadout.
@ -151,8 +153,8 @@ fn loadout_insert(
/// loadout_remove(slot, &mut loadout);
/// assert_eq!(None, loadout.active_item);
/// ```
pub fn loadout_remove(equip_slot: EquipSlot, loadout: &mut Loadout) -> Option<item::Item> {
loadout_replace(equip_slot, None, loadout)
pub fn loadout_remove(equip_slot: EquipSlot, loadout: &mut Loadout, map: &AbilityMap) -> Option<item::Item> {
loadout_replace(equip_slot, None, loadout, map)
}
/// Swap item in an inventory slot with one in a loadout slot.
@ -161,6 +163,7 @@ fn swap_inventory_loadout(
equip_slot: EquipSlot,
inventory: &mut Inventory,
loadout: &mut Loadout,
map: &AbilityMap,
) {
// Check if loadout slot can hold item
if inventory
@ -168,7 +171,7 @@ fn swap_inventory_loadout(
.map_or(true, |item| equip_slot.can_hold(&item.kind()))
{
// Take item from loadout
let from_equip = loadout_remove(equip_slot, loadout);
let from_equip = loadout_remove(equip_slot, loadout, map);
// Swap with item in the inventory
let from_inv = if let Some(item) = from_equip {
// If this fails and we get item back as an err it will just be put back in the
@ -179,14 +182,14 @@ fn swap_inventory_loadout(
};
// Put item from the inventory in loadout
if let Some(item) = from_inv {
loadout_insert(equip_slot, item, loadout).unwrap_none(); // Can never fail
loadout_insert(equip_slot, item, loadout, map).unwrap_none(); // Can never fail
}
}
}
/// Swap items in loadout. Does nothing if items are not compatible with their
/// new slots.
fn swap_loadout(slot_a: EquipSlot, slot_b: EquipSlot, loadout: &mut Loadout) {
fn swap_loadout(slot_a: EquipSlot, slot_b: EquipSlot, loadout: &mut Loadout, map: &AbilityMap) {
// Ensure that the slots are not the same
if slot_a == slot_b {
warn!("Tried to swap equip slot with itself");
@ -194,19 +197,19 @@ fn swap_loadout(slot_a: EquipSlot, slot_b: EquipSlot, loadout: &mut Loadout) {
}
// Get items from the slots
let item_a = loadout_remove(slot_a, loadout);
let item_b = loadout_remove(slot_b, loadout);
let item_a = loadout_remove(slot_a, loadout, map);
let item_b = loadout_remove(slot_b, loadout, map);
// Check if items can go in the other slots
if item_a.as_ref().map_or(true, |i| slot_b.can_hold(&i.kind()))
&& item_b.as_ref().map_or(true, |i| slot_a.can_hold(&i.kind()))
{
// Swap
loadout_replace(slot_b, item_a, loadout).unwrap_none();
loadout_replace(slot_a, item_b, loadout).unwrap_none();
loadout_replace(slot_b, item_a, loadout, map).unwrap_none();
loadout_replace(slot_a, item_b, loadout, map).unwrap_none();
} else {
// Otherwise put the items back
loadout_replace(slot_a, item_a, loadout).unwrap_none();
loadout_replace(slot_b, item_b, loadout).unwrap_none();
loadout_replace(slot_a, item_a, loadout, map).unwrap_none();
loadout_replace(slot_b, item_b, loadout, map).unwrap_none();
}
}
@ -219,6 +222,7 @@ pub fn swap(
slot_b: Slot,
inventory: Option<&mut Inventory>,
loadout: Option<&mut Loadout>,
map: &AbilityMap,
) {
match (slot_a, slot_b) {
(Slot::Inventory(slot_a), Slot::Inventory(slot_b)) => {
@ -227,12 +231,12 @@ pub fn swap(
(Slot::Inventory(inv_slot), Slot::Equip(equip_slot))
| (Slot::Equip(equip_slot), Slot::Inventory(inv_slot)) => {
if let Some((inventory, loadout)) = loadout.and_then(|l| inventory.map(|i| (i, l))) {
swap_inventory_loadout(inv_slot, equip_slot, inventory, loadout);
swap_inventory_loadout(inv_slot, equip_slot, inventory, loadout, map);
}
},
(Slot::Equip(slot_a), Slot::Equip(slot_b)) => {
loadout.map(|l| swap_loadout(slot_a, slot_b, l));
loadout.map(|l| swap_loadout(slot_a, slot_b, l, map));
},
}
}
@ -261,7 +265,7 @@ pub fn swap(
/// equip(0, &mut inv, &mut loadout);
/// assert_eq!(Some(boots), loadout.foot);
/// ```
pub fn equip(slot: usize, inventory: &mut Inventory, loadout: &mut Loadout) {
pub fn equip(slot: usize, inventory: &mut Inventory, loadout: &mut Loadout, map: &AbilityMap) {
use armor::Armor;
use item::{armor::ArmorKind, ItemKind};
@ -289,10 +293,10 @@ pub fn equip(slot: usize, inventory: &mut Inventory, loadout: &mut Loadout) {
// If item is going to mainhand, put mainhand in offhand and place offhand in
// inventory
if let EquipSlot::Mainhand = equip_slot {
swap_loadout(EquipSlot::Mainhand, EquipSlot::Offhand, loadout);
swap_loadout(EquipSlot::Mainhand, EquipSlot::Offhand, loadout, map);
}
swap_inventory_loadout(slot, equip_slot, inventory, loadout);
swap_inventory_loadout(slot, equip_slot, inventory, loadout, map);
}
}
@ -322,10 +326,10 @@ pub fn equip(slot: usize, inventory: &mut Inventory, loadout: &mut Loadout) {
/// unequip(slot, &mut inv, &mut loadout);
/// assert_eq!(None, loadout.active_item);
/// ```
pub fn unequip(slot: EquipSlot, inventory: &mut Inventory, loadout: &mut Loadout) {
loadout_remove(slot, loadout) // Remove item from loadout
pub fn unequip(slot: EquipSlot, inventory: &mut Inventory, loadout: &mut Loadout, map: &AbilityMap) {
loadout_remove(slot, loadout, map) // Remove item from loadout
.and_then(|i| inventory.push(i)) // Insert into inventory
.and_then(|i| loadout_insert(slot, i, loadout)) // If that fails put back in loadout
.and_then(|i| loadout_insert(slot, i, loadout, map)) // If that fails put back in loadout
.unwrap_none(); // Never fails
}

View File

@ -1,6 +1,6 @@
use crate::comp::{
biped_large, golem,
item::{Item, ItemKind},
item::{tool::AbilityMap, Item, ItemKind},
Alignment, Body, CharacterAbility, ItemConfig, Loadout,
};
use rand::Rng;
@ -72,6 +72,7 @@ impl LoadoutBuilder {
alignment: Alignment,
mut main_tool: Option<Item>,
is_giant: bool,
map: &AbilityMap,
) -> Self {
match body {
Body::Golem(golem) => match golem.species {
@ -145,21 +146,12 @@ impl LoadoutBuilder {
};
let active_item = if let Some(ItemKind::Tool(_)) = main_tool.as_ref().map(|i| i.kind()) {
main_tool.map(ItemConfig::from)
main_tool.map(|item| ItemConfig::from((item, map)))
} else {
Some(ItemConfig {
// We need the empty item so npcs can attack
item: Item::new_from_asset_expect("common.items.weapons.empty.empty"),
ability1: Some(CharacterAbility::BasicMelee {
energy_cost: 0,
buildup_duration: 0,
swing_duration: 100,
recover_duration: 300,
base_damage: 40,
knockback: 0.0,
range: 3.5,
max_angle: 15.0,
}),
ability1: Some(CharacterAbility::default()),
ability2: None,
ability3: None,
block_ability: None,
@ -370,7 +362,7 @@ impl LoadoutBuilder {
/// abilities or their timings is desired, you should create and provide
/// the item config directly to the [active_item](#method.active_item)
/// method
pub fn default_item_config_from_item(item: Item) -> ItemConfig { ItemConfig::from(item) }
pub fn default_item_config_from_item(item: Item, map: &AbilityMap) -> ItemConfig { ItemConfig::from((item, map)) }
/// Get an item's (weapon's) default
/// [ItemConfig](../comp/struct.ItemConfig.html)
@ -378,8 +370,8 @@ impl LoadoutBuilder {
/// the default abilities for that item via the
/// [default_item_config_from_item](#method.default_item_config_from_item)
/// function
pub fn default_item_config_from_str(item_ref: &str) -> ItemConfig {
Self::default_item_config_from_item(Item::new_from_asset_expect(item_ref))
pub fn default_item_config_from_str(item_ref: &str, map: &AbilityMap) -> ItemConfig {
Self::default_item_config_from_item(Item::new_from_asset_expect(item_ref), map)
}
pub fn active_item(mut self, item: Option<ItemConfig>) -> Self {

View File

@ -1,4 +1,5 @@
use crate::{
assets::Asset,
comp,
event::{EventBus, LocalEvent, ServerEvent},
metrics::{PhysicsMetrics, SysMetrics},
@ -180,6 +181,7 @@ impl State {
ecs.insert(BlockChange::default());
ecs.insert(TerrainChanges::default());
ecs.insert(EventBus::<LocalEvent>::default());
ecs.insert(comp::item::tool::AbilityMap::load_expect_cloned("common.abilities.weapon_ability_manifest"));
// TODO: only register on the server
ecs.insert(EventBus::<ServerEvent>::default());
ecs.insert(comp::group::GroupManager::default());

View File

@ -1,6 +1,6 @@
use crate::persistence::character_loader::CharacterLoader;
use common::{
comp::{Body, Inventory, Stats},
comp::{item::tool::AbilityMap, Body, Inventory, Stats},
loadout_builder::LoadoutBuilder,
};
use specs::{Entity, ReadExpect};
@ -12,13 +12,14 @@ pub fn create_character(
character_tool: Option<String>,
body: Body,
character_loader: &ReadExpect<'_, CharacterLoader>,
map: &AbilityMap,
) {
let stats = Stats::new(character_alias.to_string(), body);
let loadout = LoadoutBuilder::new()
.defaults()
.active_item(Some(LoadoutBuilder::default_item_config_from_str(
character_tool.as_deref().unwrap(),
character_tool.as_deref().unwrap(), map
)))
.build();

View File

@ -9,7 +9,7 @@ use crate::{
use chrono::{NaiveTime, Timelike};
use common::{
cmd::{ChatCommand, CHAT_COMMANDS, CHAT_SHORTCUTS},
comp::{self, ChatType, Item, LightEmitter, WaypointArea},
comp::{self, item::tool::AbilityMap, ChatType, Item, LightEmitter, WaypointArea},
effect::Effect,
event::{EventBus, ServerEvent},
msg::{DisconnectReason, Notification, PlayerListUpdate, ServerGeneral},
@ -655,14 +655,17 @@ fn handle_spawn(
let body = body();
let map = server.state().ecs().fetch::<AbilityMap>();
let loadout = LoadoutBuilder::build_loadout(body, alignment, None, false, &map).build();
drop(map);
let mut entity_base = server
.state
.create_npc(
pos,
comp::Stats::new(get_npc_name(id).into(), body),
comp::Health::new(body, 1),
LoadoutBuilder::build_loadout(body, alignment, None, false)
.build(),
loadout,
body,
)
.with(comp::Vel(vel))

View File

@ -4,7 +4,7 @@ use crate::{
Server,
};
use common::{
comp::{self, item, Pos},
comp::{self, item::{self, tool::AbilityMap}, Pos},
consts::MAX_MOUNT_RANGE,
msg::ServerGeneral,
sync::{Uid, WorldSyncExt},
@ -103,8 +103,8 @@ pub fn handle_unmount(server: &mut Server, mounter: EcsEntity) {
#[allow(clippy::nonminimal_bool)] // TODO: Pending review in #587
pub fn handle_possess(server: &Server, possessor_uid: Uid, possesse_uid: Uid) {
let state = &server.state;
let ecs = state.ecs();
let ecs = &server.state.ecs();
let map = ecs.fetch::<AbilityMap>();
if let (Some(possessor), Some(possesse)) = (
ecs.entity_from_uid(possessor_uid.into()),
ecs.entity_from_uid(possesse_uid.into()),
@ -177,7 +177,7 @@ pub fn handle_possess(server: &Server, possessor_uid: Uid, possesse_uid: Uid) {
let item = comp::Item::new_from_asset_expect("common.items.debug.possess");
if let item::ItemKind::Tool(_) = item.kind() {
let debug_item = comp::ItemConfig::from(item);
let debug_item = comp::ItemConfig::from((item, &*map));
std::mem::swap(&mut loadout.active_item, &mut loadout.second_item);
loadout.active_item = Some(debug_item);
}

View File

@ -1,7 +1,7 @@
use crate::{client::Client, Server, StateExt};
use common::{
comp::{
self, item,
self, item::{self, tool::AbilityMap},
slot::{self, Slot},
Pos,
},
@ -203,7 +203,8 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv
if let Some(lantern) = lantern_opt {
swap_lantern(&mut state.ecs().write_storage(), entity, &lantern);
}
slot::equip(slot, inventory, loadout);
let map = state.ecs().fetch::<AbilityMap>();
slot::equip(slot, inventory, loadout, &map);
Some(comp::InventoryUpdateEvent::Used)
} else {
None
@ -338,7 +339,8 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv
if slot == slot::EquipSlot::Lantern {
snuff_lantern(&mut state.ecs().write_storage(), entity);
}
slot::unequip(slot, inventory, loadout);
let map = state.ecs().fetch::<AbilityMap>();
slot::unequip(slot, inventory, loadout, &map);
Some(comp::InventoryUpdateEvent::Used)
} else {
error!(?entity, "Entity doesn't have a loadout, can't unequip...");
@ -364,12 +366,14 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv
let mut loadouts = ecs.write_storage();
let inventory = inventories.get_mut(entity);
let loadout = loadouts.get_mut(entity);
let map = state.ecs().fetch::<AbilityMap>();
slot::swap(a, b, inventory, loadout);
slot::swap(a, b, inventory, loadout, &map);
// :/
drop(loadouts);
drop(inventories);
drop(map);
state.write_component(
entity,
@ -378,6 +382,7 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv
},
comp::InventoryManip::Drop(slot) => {
let map = state.ecs().fetch::<AbilityMap>();
let item = match slot {
Slot::Inventory(slot) => state
.ecs()
@ -388,8 +393,9 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv
.ecs()
.write_storage()
.get_mut(entity)
.and_then(|ldt| slot::loadout_remove(slot, ldt)),
.and_then(|ldt| slot::loadout_remove(slot, ldt, &map)),
};
drop(map);
// FIXME: We should really require the drop and write to be atomic!
if let (Some(mut item), Some(pos)) =

View File

@ -46,7 +46,7 @@ use crate::{
};
use common::{
cmd::ChatCommand,
comp::{self, ChatType},
comp::{self, item::tool::AbilityMap, ChatType},
event::{EventBus, ServerEvent},
msg::{
ClientType, DisconnectReason, ServerGeneral, ServerInfo, ServerInit, ServerMsg, WorldMapMsg,
@ -159,9 +159,9 @@ impl Server {
state
.ecs_mut()
.insert(CharacterUpdater::new(&persistence_db_dir)?);
state
.ecs_mut()
.insert(CharacterLoader::new(&persistence_db_dir)?);
let character_loader = CharacterLoader::new(&persistence_db_dir, &*state.ecs().fetch::<AbilityMap>());
state.ecs_mut().insert(character_loader);
state.ecs_mut().insert(Vec::<Outcome>::new());
// System timers for performance monitoring

View File

@ -25,6 +25,7 @@ use crate::{
};
use common::{
character::{CharacterId, CharacterItem, MAX_CHARACTERS_PER_PLAYER},
comp::item::tool::AbilityMap,
state::Time,
};
use core::ops::Range;
@ -60,6 +61,7 @@ pub fn load_character_data(
requesting_player_uuid: String,
char_id: CharacterId,
connection: VelorenTransaction,
map: &AbilityMap,
) -> CharacterDataResult {
use schema::{body::dsl::*, character::dsl::*, item::dsl::*, stats::dsl::*};
@ -102,7 +104,7 @@ pub fn load_character_data(
convert_body_from_database(&char_body)?,
convert_stats_from_database(&stats_data, character_data.alias),
convert_inventory_from_database_items(&inventory_items)?,
convert_loadout_from_database_items(&loadout_items)?,
convert_loadout_from_database_items(&loadout_items, map)?,
waypoint,
))
}
@ -117,6 +119,7 @@ pub fn load_character_data(
pub fn load_character_list(
player_uuid_: &str,
connection: VelorenTransaction,
map: &AbilityMap,
) -> CharacterListResult {
use schema::{body::dsl::*, character::dsl::*, item::dsl::*, stats::dsl::*};
@ -149,7 +152,7 @@ pub fn load_character_list(
.filter(parent_container_item_id.eq(loadout_container_id))
.load::<Item>(&*connection)?;
let loadout = convert_loadout_from_database_items(&loadout_items)?;
let loadout = convert_loadout_from_database_items(&loadout_items, map)?;
Ok(CharacterItem {
character: char,
@ -166,6 +169,7 @@ pub fn create_character(
character_alias: &str,
persisted_components: PersistedComponents,
connection: VelorenTransaction,
map: &AbilityMap,
) -> CharacterListResult {
use schema::item::dsl::*;
@ -299,7 +303,7 @@ pub fn create_character(
)));
}
load_character_list(uuid, connection)
load_character_list(uuid, connection, map)
}
/// Delete a character. Returns the updated character list.
@ -307,6 +311,7 @@ pub fn delete_character(
requesting_player_uuid: &str,
char_id: CharacterId,
connection: VelorenTransaction,
map: &AbilityMap,
) -> CharacterListResult {
use schema::{body::dsl::*, character::dsl::*, stats::dsl::*};
@ -387,7 +392,7 @@ pub fn delete_character(
)));
}
load_character_list(requesting_player_uuid, connection)
load_character_list(requesting_player_uuid, connection, map)
}
/// Before creating a character, we ensure that the limit on the number of

View File

@ -9,7 +9,7 @@ use crate::persistence::{
};
use common::{
character::CharacterId,
comp::{Body as CompBody, *},
comp::{item::tool::AbilityMap, Body as CompBody, *},
loadout_builder,
};
use core::{convert::TryFrom, num::NonZeroU64};
@ -240,7 +240,7 @@ pub fn convert_inventory_from_database_items(database_items: &[Item]) -> Result<
Ok(inventory)
}
pub fn convert_loadout_from_database_items(database_items: &[Item]) -> Result<Loadout, Error> {
pub fn convert_loadout_from_database_items(database_items: &[Item], map: &AbilityMap) -> Result<Loadout, Error> {
let mut loadout = loadout_builder::LoadoutBuilder::new();
for db_item in database_items.iter() {
let item = common::comp::Item::new_from_asset(db_item.item_definition_id.as_str())?;
@ -251,8 +251,8 @@ pub fn convert_loadout_from_database_items(database_items: &[Item]) -> Result<Lo
)?));
match db_item.position.as_str() {
"active_item" => loadout = loadout.active_item(Some(ItemConfig::from(item))),
"second_item" => loadout = loadout.second_item(Some(ItemConfig::from(item))),
"active_item" => loadout = loadout.active_item(Some(ItemConfig::from((item, map)))),
"second_item" => loadout = loadout.second_item(Some(ItemConfig::from((item, map)))),
"lantern" => loadout = loadout.lantern(Some(item)),
"shoulder" => loadout = loadout.shoulder(Some(item)),
"chest" => loadout = loadout.chest(Some(item)),

View File

@ -3,7 +3,10 @@ use crate::persistence::{
error::Error,
establish_connection, PersistedComponents,
};
use common::character::{CharacterId, CharacterItem};
use common::{
character::{CharacterId, CharacterItem},
comp::item::tool::AbilityMap,
};
use crossbeam::{channel, channel::TryIter};
use std::path::Path;
use tracing::error;
@ -65,12 +68,14 @@ pub struct CharacterLoader {
}
impl CharacterLoader {
pub fn new(db_dir: &Path) -> diesel::QueryResult<Self> {
pub fn new(db_dir: &Path, map: &AbilityMap) -> diesel::QueryResult<Self> {
let (update_tx, internal_rx) = channel::unbounded::<CharacterLoaderRequest>();
let (internal_tx, update_rx) = channel::unbounded::<CharacterLoaderResponse>();
let mut conn = establish_connection(db_dir)?;
let map = map.clone();
std::thread::spawn(move || {
for request in internal_rx {
let (entity, kind) = request;
@ -88,6 +93,7 @@ impl CharacterLoader {
&character_alias,
persisted_components,
txn,
&map,
)
})),
CharacterLoaderRequestKind::DeleteCharacter {
@ -95,12 +101,12 @@ impl CharacterLoader {
character_id,
} => {
CharacterLoaderResponseType::CharacterList(conn.transaction(|txn| {
delete_character(&player_uuid, character_id, txn)
delete_character(&player_uuid, character_id, txn, &map)
}))
},
CharacterLoaderRequestKind::LoadCharacterList { player_uuid } => {
CharacterLoaderResponseType::CharacterList(
conn.transaction(|txn| load_character_list(&player_uuid, txn)),
conn.transaction(|txn| load_character_list(&player_uuid, txn, &map)),
)
},
CharacterLoaderRequestKind::LoadCharacterData {
@ -108,7 +114,7 @@ impl CharacterLoader {
character_id,
} => {
CharacterLoaderResponseType::CharacterData(Box::new(conn.transaction(
|txn| load_character_data(player_uuid, character_id, txn),
|txn| load_character_data(player_uuid, character_id, txn, &map),
)))
},
},

View File

@ -4,7 +4,7 @@ use crate::{
persistence::character_loader::CharacterLoader, presence::Presence, EditableSettings,
};
use common::{
comp::{ChatType, Player, UnresolvedChatMsg},
comp::{item::tool::AbilityMap, ChatType, Player, UnresolvedChatMsg},
event::{EventBus, ServerEvent},
msg::{ClientGeneral, ServerGeneral},
span,
@ -28,6 +28,7 @@ impl Sys {
editable_settings: &ReadExpect<'_, EditableSettings>,
alias_validator: &ReadExpect<'_, AliasValidator>,
msg: ClientGeneral,
map: &AbilityMap,
) -> Result<(), crate::error::Error> {
match msg {
// Request spectator state
@ -101,6 +102,7 @@ impl Sys {
tool,
body,
character_loader,
map,
);
}
},
@ -134,6 +136,7 @@ impl<'a> System<'a> for Sys {
ReadStorage<'a, Presence>,
ReadExpect<'a, EditableSettings>,
ReadExpect<'a, AliasValidator>,
ReadExpect<'a, AbilityMap>,
);
fn run(
@ -149,6 +152,7 @@ impl<'a> System<'a> for Sys {
presences,
editable_settings,
alias_validator,
map,
): Self::SystemData,
) {
span!(_guard, "run", "msg::character_screen::Sys::run");
@ -171,6 +175,7 @@ impl<'a> System<'a> for Sys {
&editable_settings,
&alias_validator,
msg,
&map,
)
});
}

View File

@ -1,7 +1,7 @@
use super::SysTimer;
use crate::{chunk_generator::ChunkGenerator, client::Client, presence::Presence, Tick};
use common::{
comp::{self, bird_medium, Alignment, Pos},
comp::{self, bird_medium, item::tool::AbilityMap, Alignment, Pos},
event::{EventBus, ServerEvent},
generation::get_npc_name,
msg::ServerGeneral,
@ -12,7 +12,7 @@ use common::{
LoadoutBuilder,
};
use rand::Rng;
use specs::{Join, Read, ReadStorage, System, Write, WriteExpect};
use specs::{Join, Read, ReadExpect, ReadStorage, System, Write, WriteExpect};
use std::sync::Arc;
use vek::*;
@ -35,6 +35,7 @@ impl<'a> System<'a> for Sys {
ReadStorage<'a, Pos>,
ReadStorage<'a, Presence>,
ReadStorage<'a, Client>,
ReadExpect<'a, AbilityMap>,
);
fn run(
@ -49,6 +50,7 @@ impl<'a> System<'a> for Sys {
positions,
presences,
clients,
map,
): Self::SystemData,
) {
span!(_guard, "run", "terrain::Sys::run");
@ -143,7 +145,7 @@ impl<'a> System<'a> for Sys {
}
let loadout =
LoadoutBuilder::build_loadout(body, alignment, main_tool, entity.is_giant)
LoadoutBuilder::build_loadout(body, alignment, main_tool, entity.is_giant, &map)
.build();
let health = comp::Health::new(stats.body_type, stats.level.level());

View File

@ -39,7 +39,7 @@ use crate::{
settings::Settings,
window::{Event, Window},
};
use common::{assets::watch, clock::Clock};
use common::{assets::watch, clock::Clock, comp::item::tool::AbilityMap};
/// A type used to store state that is shared between all play states.
pub struct GlobalState {
@ -110,7 +110,7 @@ pub trait PlayState {
fn enter(&mut self, global_state: &mut GlobalState, direction: Direction);
/// Tick the play state
fn tick(&mut self, global_state: &mut GlobalState, events: Vec<Event>) -> PlayStateResult;
fn tick(&mut self, global_state: &mut GlobalState, events: Vec<Event>, map: &AbilityMap) -> PlayStateResult;
/// Get a descriptive name for this state type.
fn name(&self) -> &'static str;

View File

@ -192,5 +192,5 @@ fn main() {
localization_watcher,
};
run::run(global_state, event_loop);
run::run(global_state, event_loop, map);
}

View File

@ -10,7 +10,7 @@ use crate::{
Direction, GlobalState, PlayState, PlayStateResult,
};
use client::{self, Client};
use common::{assets::Asset, comp, span, state::DeltaTime};
use common::{assets::Asset, comp::{self, item::tool::AbilityMap}, span, state::DeltaTime};
use specs::WorldExt;
use std::{cell::RefCell, rc::Rc};
use tracing::error;
@ -62,7 +62,7 @@ impl PlayState for CharSelectionState {
self.client.borrow_mut().load_character_list();
}
fn tick(&mut self, global_state: &mut GlobalState, events: Vec<WinEvent>) -> PlayStateResult {
fn tick(&mut self, global_state: &mut GlobalState, events: Vec<WinEvent>, map: &AbilityMap) -> PlayStateResult {
span!(_guard, "tick", "<CharSelectionState as PlayState>::tick");
let (client_presence, client_registered) = {
let client = self.client.borrow();
@ -88,7 +88,7 @@ impl PlayState for CharSelectionState {
// Maintain the UI.
let events = self
.char_selection_ui
.maintain(global_state, &mut self.client.borrow_mut());
.maintain(global_state, &mut self.client.borrow_mut(), map);
for event in events {
match event {

View File

@ -24,7 +24,7 @@ use client::Client;
use common::{
assets::Asset,
character::{CharacterId, CharacterItem, MAX_CHARACTERS_PER_PLAYER},
comp::{self, humanoid},
comp::{self, humanoid, item::tool::AbilityMap},
LoadoutBuilder,
};
//ImageFrame, Tooltip,
@ -179,12 +179,12 @@ impl Mode {
}
}
pub fn create(name: String) -> Self {
pub fn create(name: String, map: &AbilityMap) -> Self {
let tool = STARTER_SWORD;
let loadout = LoadoutBuilder::new()
.defaults()
.active_item(Some(LoadoutBuilder::default_item_config_from_str(tool)))
.active_item(Some(LoadoutBuilder::default_item_config_from_str(tool, map)))
.build();
let loadout = Box::new(loadout);
@ -1172,7 +1172,7 @@ impl Controls {
.into()
}
fn update(&mut self, message: Message, events: &mut Vec<Event>, characters: &[CharacterItem]) {
fn update(&mut self, message: Message, events: &mut Vec<Event>, characters: &[CharacterItem], map: &AbilityMap) {
match message {
Message::Back => {
if matches!(&self.mode, Mode::Create { .. }) {
@ -1206,7 +1206,7 @@ impl Controls {
},
Message::NewCharacter => {
if matches!(&self.mode, Mode::Select { .. }) {
self.mode = Mode::create(String::new());
self.mode = Mode::create(String::new(), map);
}
},
Message::CreateCharacter => {
@ -1242,7 +1242,7 @@ impl Controls {
Message::Tool(value) => {
if let Mode::Create { tool, loadout, .. } = &mut self.mode {
*tool = value;
loadout.active_item = Some(LoadoutBuilder::default_item_config_from_str(*tool));
loadout.active_item = Some(LoadoutBuilder::default_item_config_from_str(*tool, map));
}
},
Message::RandomizeCharacter => {
@ -1411,7 +1411,7 @@ impl CharSelectionUi {
}
// TODO: do we need whole client here or just character list?
pub fn maintain(&mut self, global_state: &mut GlobalState, client: &mut Client) -> Vec<Event> {
pub fn maintain(&mut self, global_state: &mut GlobalState, client: &mut Client, map: &AbilityMap) -> Vec<Event> {
let mut events = Vec::new();
let (mut messages, _) = self.ui.maintain(
@ -1426,7 +1426,7 @@ impl CharSelectionUi {
messages.into_iter().for_each(|message| {
self.controls
.update(message, &mut events, &client.character_list.characters)
.update(message, &mut events, &client.character_list.characters, map)
});
events

View File

@ -12,7 +12,7 @@ use crate::{
Direction, GlobalState, PlayState, PlayStateResult,
};
use client_init::{ClientInit, Error as InitError, Msg as InitMsg};
use common::{assets::Asset, comp, span};
use common::{assets::Asset, comp::{self, item::tool::AbilityMap}, span};
use tracing::error;
use ui::{Event as MainMenuEvent, MainMenuUi};
@ -48,7 +48,7 @@ impl PlayState for MainMenuState {
}
}
fn tick(&mut self, global_state: &mut GlobalState, events: Vec<Event>) -> PlayStateResult {
fn tick(&mut self, global_state: &mut GlobalState, events: Vec<Event>, map: &AbilityMap) -> PlayStateResult {
span!(_guard, "tick", "<MainMenuState as PlayState>::tick");
let mut localized_strings = crate::i18n::Localization::load_expect(
&crate::i18n::i18n_asset_key(&global_state.settings.language.selected_language),
@ -263,7 +263,7 @@ impl PlayState for MainMenuState {
},
#[cfg(feature = "singleplayer")]
MainMenuEvent::StartSingleplayer => {
let singleplayer = Singleplayer::new(None); // TODO: Make client and server use the same thread pool
let singleplayer = Singleplayer::new(None, map); // TODO: Make client and server use the same thread pool
global_state.singleplayer = Some(singleplayer);
},

View File

@ -4,11 +4,11 @@ use crate::{
window::{Event, EventLoop},
Direction, GlobalState, PlayState, PlayStateResult,
};
use common::{no_guard_span, span, util::GuardlessSpan};
use common::{comp::item::tool::AbilityMap, no_guard_span, span, util::GuardlessSpan};
use std::{mem, time::Duration};
use tracing::debug;
pub fn run(mut global_state: GlobalState, event_loop: EventLoop) {
pub fn run(mut global_state: GlobalState, event_loop: EventLoop, map: &AbilityMap) {
// Set up the initial play state.
let mut states: Vec<Box<dyn PlayState>> = vec![Box::new(MainMenuState::new(&mut global_state))];
states.last_mut().map(|current_state| {
@ -26,6 +26,8 @@ pub fn run(mut global_state: GlobalState, event_loop: EventLoop) {
let mut poll_span = None;
let mut event_span = None;
let map = map.clone();
event_loop.run(move |event, _, control_flow| {
// Continuously run loop since we handle sleeping
*control_flow = winit::event_loop::ControlFlow::Poll;
@ -53,7 +55,7 @@ pub fn run(mut global_state: GlobalState, event_loop: EventLoop) {
event_span.take();
poll_span.take();
if polled_twice {
handle_main_events_cleared(&mut states, control_flow, &mut global_state);
handle_main_events_cleared(&mut states, control_flow, &mut global_state, &map);
}
poll_span = Some(no_guard_span!("Poll Winit"));
polled_twice = !polled_twice;
@ -82,6 +84,7 @@ fn handle_main_events_cleared(
states: &mut Vec<Box<dyn PlayState>>,
control_flow: &mut winit::event_loop::ControlFlow,
global_state: &mut GlobalState,
map: &AbilityMap,
) {
span!(guard, "Handle MainEventsCleared");
// Screenshot / Fullscreen toggle
@ -102,7 +105,7 @@ fn handle_main_events_cleared(
let mut exit = true;
while let Some(state_result) = states.last_mut().map(|last| {
let events = global_state.window.fetch_events();
last.tick(global_state, events)
last.tick(global_state, events, map)
}) {
// Implement state transfer logic.
match state_result {

View File

@ -15,7 +15,7 @@ use client::{self, Client};
use common::{
assets::Asset,
comp,
comp::{ChatMsg, ChatType, InventoryUpdateEvent, Pos, Vel},
comp::{item::tool::AbilityMap, ChatMsg, ChatType, InventoryUpdateEvent, Pos, Vel},
consts::{MAX_MOUNT_RANGE, MAX_PICKUP_RANGE},
event::EventBus,
msg::PresenceKind,
@ -203,7 +203,7 @@ impl PlayState for SessionState {
}
}
fn tick(&mut self, global_state: &mut GlobalState, events: Vec<Event>) -> PlayStateResult {
fn tick(&mut self, global_state: &mut GlobalState, events: Vec<Event>, map: &AbilityMap) -> PlayStateResult {
span!(_guard, "tick", "<Session as PlayState>::tick");
// TODO: let mut client = self.client.borrow_mut();
// NOTE: Not strictly necessary, but useful for hotloading translation changes.

View File

@ -1,5 +1,5 @@
use client::Client;
use common::clock::Clock;
use common::{clock::Clock, comp::item::tool::AbilityMap};
use crossbeam::channel::{bounded, unbounded, Receiver, Sender, TryRecvError};
use server::{Error as ServerError, Event, Input, Server};
use std::{
@ -31,7 +31,7 @@ pub struct Singleplayer {
}
impl Singleplayer {
pub fn new(client: Option<&Client>) -> Self {
pub fn new(client: Option<&Client>,map: &AbilityMap) -> Self {
let (sender, receiver) = unbounded();
// Determine folder to save server data in