From ba41016849496ecdf48784356c8b21eee6dbe0e2 Mon Sep 17 00:00:00 2001 From: Faye Date: Tue, 15 Sep 2026 16:22:20 +0200 Subject: [PATCH 1/4] vanilla koth rewards limit koth rewards to be class specific and legitimately obtainable items in vanilla Cube World + added automatic power level to koth and /give --- protocol/src/packet/common/item/kind.rs | 8 +++ .../addon/command_manager/commands/give.rs | 2 +- server/src/addon/events/legacy_koth.rs | 57 +++++++++++++------ server/src/addon/events/shop.rs | 9 +-- 4 files changed, 50 insertions(+), 26 deletions(-) diff --git a/protocol/src/packet/common/item/kind.rs b/protocol/src/packet/common/item/kind.rs index 44caab0..f69d629 100644 --- a/protocol/src/packet/common/item/kind.rs +++ b/protocol/src/packet/common/item/kind.rs @@ -1,6 +1,7 @@ use strum_macros::*; use crate::packet::common::item::Kind; +use crate::utils::max_level_of; impl Kind { #[must_use] @@ -41,6 +42,13 @@ impl Kind { Self::Weapon(_) | Self::Chest | Self::Gloves | Self::Boots | Self::Shoulder | Self::Amulet | Self::Ring ) } + + #[must_use] + pub fn item_level(&self, player_level: i16) -> i16 { + if self.uses_power() { max_level_of(player_level.into()) as i16 } + else if self.uses_level() { player_level } + else { 1 } + } } #[repr(u8)] diff --git a/server/src/addon/command_manager/commands/give.rs b/server/src/addon/command_manager/commands/give.rs index c391405..bcde72f 100644 --- a/server/src/addon/command_manager/commands/give.rs +++ b/server/src/addon/command_manager/commands/give.rs @@ -45,7 +45,7 @@ impl Command for super::Give { let valid_materials = by_item_kind(item.kind); if item.kind.uses_rarity() {item.rarity = LEGENDARY} else {item.rarity = NORMAL} - if item.kind.uses_level() {item.level = caller.character.read().await.level as i16} else {item.level = 1} + item.level = item.kind.item_level(caller.character.read().await.level as i16); if !valid_materials.is_empty() {item.material = valid_materials[0]} for param in params { diff --git a/server/src/addon/events/legacy_koth.rs b/server/src/addon/events/legacy_koth.rs index 3c642c7..046d9f7 100644 --- a/server/src/addon/events/legacy_koth.rs +++ b/server/src/addon/events/legacy_koth.rs @@ -4,7 +4,7 @@ use std::sync::Arc; use std::time::Duration; use config::{Config, ConfigError}; -use rand::random; +use rand::{random, random_range}; use strum::IntoEnumIterator; use tap::Tap; use tokio::sync::RwLock; @@ -12,8 +12,8 @@ use tokio::time::sleep; use protocol::nalgebra::Point3; use protocol::packet::{ChatMessageFromServer, CreatureUpdate, WorldUpdate}; -use protocol::packet::common::{CreatureId, EulerAngles, Hitbox, Item, Race, item::{Kind, kind}}; -use protocol::packet::creature_update::Affiliation; +use protocol::packet::common::{CreatureId, EulerAngles, Hitbox, Item, Race, item::{Kind, Material, kind}}; +use protocol::packet::creature_update::{Affiliation, Occupation}; use protocol::packet::world_update::{Mission, Pickup, sound}; use protocol::packet::world_update::mission::{Objective, State}; use protocol::utils::constants::{SIZE_BLOCK, SIZE_ZONE, SIZE_SECTOR}; @@ -194,23 +194,47 @@ async fn handle_points(player: &Player, threshold: Option, reward: bool) { } async fn give_reward(player: &Player) { - let level = player.character.read().await.level as i16; - let pickup = Pickup { interactor: player.id, item: reward_item(level) }; + let (level, occupation) = { + let character = player.character.read().await; + (character.level as i16, character.occupation) + }; + let pickup = Pickup { interactor: player.id, item: reward_item(level, occupation) }; play_sound_at_player(player, sound::Kind::Missioncomplete, 0.62, 1.0).await; player.send_ignoring(&WorldUpdate::from(pickup)).await; } -fn reward_item(level: i16) -> Item { +type ClassWeapons = &'static [(kind::Weapon, &'static [Material])]; + +fn class_gear(occupation: Occupation) -> Option<(Material, ClassWeapons)> { + use kind::Weapon::{Axe, Boomerang, Bow, Bracelet, Crossbow, Dagger, Fist, Greataxe, Greatmace, Greatsword, Longsword, Mace, Shield, Staff, Sword, Wand}; + use Material::{Cotton, Gold, Iron, Linen, Silk, Silver, Wood}; + + match occupation { + Occupation::Warrior => Some((Iron, &[(Sword, &[Iron]), (Axe, &[Iron]), (Mace, &[Iron]), (Shield, &[Iron]), + (Greatsword, &[Iron]), (Greataxe, &[Iron]), (Greatmace, &[Iron, Wood])])), + Occupation::Ranger => Some((Linen, &[(Bow, &[Wood]), (Crossbow, &[Wood]), (Boomerang, &[Wood])])), + Occupation::Mage => Some((Silk, &[(Wand, &[Wood]), (Staff, &[Wood]), (Bracelet, &[Gold, Silver])])), + Occupation::Rogue => Some((Cotton, &[(Longsword, &[Iron]), (Dagger, &[Iron]), (Fist, &[Iron])])), + _ => None + } +} + +fn reward_item(level: i16, occupation: Occupation) -> Item { let mut item = Item::default(); - item.kind = match random::() { - roll if roll < REWARD_WEAPON_ODDS - => Kind::Weapon(pick_from(&kind::Weapon::iter().collect::>())), - roll if roll < REWARD_WEAPON_ODDS + REWARD_ARMOR_ODDS - => pick_from(&[Kind::Chest, Kind::Gloves, Kind::Boots, Kind::Shoulder]), - roll if roll < REWARD_WEAPON_ODDS + REWARD_ARMOR_ODDS + REWARD_SPIRIT_ODDS - => Kind::Resource(kind::Resource::Spirit), - _ => Kind::Pet(pick_from(&Race::iter().collect::>())) + let roll = random::(); + (item.kind, item.material) = match class_gear(occupation) { + Some((_, weapons)) if roll < REWARD_WEAPON_ODDS => { + let (weapon, materials) = pick_from(weapons); + (Kind::Weapon(weapon), pick_from(materials)) + } + Some((armor, _)) if roll < REWARD_WEAPON_ODDS + REWARD_ARMOR_ODDS + => (pick_from(&[Kind::Chest, Kind::Gloves, Kind::Boots, Kind::Shoulder]), armor), + _ => { + let kind = if roll < REWARD_WEAPON_ODDS + REWARD_ARMOR_ODDS + REWARD_SPIRIT_ODDS { Kind::Resource(kind::Resource::Spirit) } + else { Kind::Pet(pick_from(&Race::iter().collect::>())) }; + (kind, pick_from(by_item_kind(kind))) + } }; item.rarity = match item.kind { @@ -220,9 +244,8 @@ fn reward_item(level: i16) -> Item { _ => if item.kind.uses_rarity() { LEGENDARY } else { NORMAL } }; - item.material = pick_from(by_item_kind(item.kind)); - item.level = if item.kind.uses_level() { level } else { 1 }; - item.seed = if item.uses_seed() { random() } else { 0 }; + item.level = item.kind.item_level(level); + item.seed = if item.uses_seed() { random_range(0..=i32::MAX) } else { 0 }; item } diff --git a/server/src/addon/events/shop.rs b/server/src/addon/events/shop.rs index d6a23a7..72b471a 100644 --- a/server/src/addon/events/shop.rs +++ b/server/src/addon/events/shop.rs @@ -17,7 +17,6 @@ use protocol::packet::WorldUpdate; use protocol::packet::CreatureUpdate; use protocol::utils::constants::{materials, SIZE_BLOCK, SIZE_ZONE}; use protocol::utils::constants::rarity::*; -use protocol::utils::max_level_of; use crate::addon::events::utils::{appearance_invisible, config_fallback, config_optional, creatures_circular, NAME_OVERFLOW}; use crate::addon::play_sound_at_player; @@ -306,7 +305,7 @@ fn item_validation(state: &mut State, item: &mut Item, player_level: i16) -> Res State::Rarity => {if item.kind.uses_rarity() { return Ok(()) } item.rarity = NORMAL; *state = State::Model;} - State::Model => {item.level = item_level(item.kind, player_level); + State::Model => {item.level = item.kind.item_level(player_level); if item.uses_models() { return Ok(()) } item.seed = 0; *state = State::Stat;} @@ -317,12 +316,6 @@ fn item_validation(state: &mut State, item: &mut Item, player_level: i16) -> Res } } -fn item_level(kind: Kind, player_level: i16) -> i16 { - if kind.uses_power() { max_level_of(player_level.into()) as i16 } - else if kind.uses_level() { player_level } - else { 1 } -} - fn item_name(kind: Kind) -> String { let subtype = subtype_names(kind); if subtype.is_empty() { kind.to_string() } else { subtype } From da8731cd9f40dabb374dfc31a71df8e29a55837b Mon Sep 17 00:00:00 2001 From: Faye Date: Tue, 15 Sep 2026 19:18:26 +0200 Subject: [PATCH 2/4] koth reward weights reward odds are now configurable in the config file + added Amulet, Ring, and Leftovers to the pool --- server/src/addon/events/legacy_koth.rs | 103 ++++++++++++++++++++----- 1 file changed, 84 insertions(+), 19 deletions(-) diff --git a/server/src/addon/events/legacy_koth.rs b/server/src/addon/events/legacy_koth.rs index 046d9f7..e2fc575 100644 --- a/server/src/addon/events/legacy_koth.rs +++ b/server/src/addon/events/legacy_koth.rs @@ -4,8 +4,9 @@ use std::sync::Arc; use std::time::Duration; use config::{Config, ConfigError}; -use rand::{random, random_range}; -use strum::IntoEnumIterator; +use rand::{random_range, rng}; +use rand::seq::IndexedRandom; +use serde::Deserialize; use tap::Tap; use tokio::sync::RwLock; use tokio::time::sleep; @@ -18,7 +19,7 @@ use protocol::packet::world_update::{Mission, Pickup, sound}; use protocol::packet::world_update::mission::{Objective, State}; use protocol::utils::constants::{SIZE_BLOCK, SIZE_ZONE, SIZE_SECTOR}; use protocol::utils::constants::materials::by_item_kind; -use protocol::utils::constants::rarity::{NORMAL, RARE, EPIC, LEGENDARY}; +use protocol::utils::constants::rarity::{NORMAL, UNCOMMON, RARE, EPIC, LEGENDARY}; use crate::addon::events::utils::{appearance_invisible, config_fallback, config_optional, creatures_circular, is_in_zone, pick_from, NAME_OVERFLOW, RENDER_DISTANCE_CREATURE}; use crate::addon::play_sound_at_player; @@ -33,9 +34,14 @@ const TORCHES_ID: i64 = 75000; const REWARD_POINTS: i32 = 10000; const REWARD_THRESHOLDS: [i32; 4] = [20, 40, 60, 80]; -const REWARD_WEAPON_ODDS: f32 = 0.30; -const REWARD_ARMOR_ODDS: f32 = 0.30; -const REWARD_SPIRIT_ODDS: f32 = 0.35; +const VANILLA_PETS: &[Race] = { + use Race::*; + &[Collie, Alpaca, AlpacaBrown, Turtle, Terrier, TerrierScottish, Cat, Pig, Sheep, Bunny, Porcupine, + SlimeGreen, SlimePink, SlimeYellow, SlimeBlue, Monkey, Hornet, Crow, Chicken, Seagull, Parrot, Bat, + Fly, Midge, Mosquito, RunnerPlain, RunnerLeaf, RunnerSnow, RunnerDesert, Peacock, Duckbill, Crocodile, + Spitter, Mole, Biter, Squirrel, Raccoon, Owl, Penguin, Horse, Camel, BeetleDark, BeetleFire, + BeetleSnout, BeetleLemon, Crab, Bumblebee] +}; #[derive(Debug)] pub struct LegacyKoth { @@ -50,7 +56,9 @@ pub struct LegacyKoth { kill_king_points: i32, kill_king_xp: i32, kill_points: i32, - kill_xp: i32 + kill_xp: i32, + loot: LootWeights, + rarity: RarityWeights } impl LegacyKoth { @@ -63,6 +71,11 @@ impl LegacyKoth { let reward_frequency: i32 = config_fallback(config, "legacykoth.reward_frequency", 420_i32)?; let king_reward_frequency: i32 = config_fallback(config, "legacykoth.king_reward_frequency", 180_i32)?; + let loot: LootWeights = config_fallback(config, "legacykoth.loot", LootWeights::default())?; + let rarity: RarityWeights = config_fallback(config, "legacykoth.rarity", RarityWeights::default())?; + if loot.table().iter().all(|&(_, weight)| weight == 0) { return Err(ConfigError::Message("legacykoth.loot needs a non-zero weight".into())) } + if rarity.table().iter().all(|&(_, weight)| weight == 0) { return Err(ConfigError::Message("legacykoth.rarity needs a non-zero weight".into())) } + Ok(Self { points: RwLock::new(HashMap::new()), center, @@ -75,7 +88,9 @@ impl LegacyKoth { kill_king_points: config_fallback(config, "legacykoth.kill_king_points", 500_i32)?, kill_king_xp: config_fallback(config, "legacykoth.kill_king_xp", 20_i32)?, kill_points: config_fallback(config, "legacykoth.kill_points", 200_i32)?, - kill_xp: config_fallback(config, "legacykoth.kill_xp", 10_i32)? + kill_xp: config_fallback(config, "legacykoth.kill_xp", 10_i32)?, + loot, + rarity }) } } @@ -198,11 +213,51 @@ async fn give_reward(player: &Player) { let character = player.character.read().await; (character.level as i16, character.occupation) }; - let pickup = Pickup { interactor: player.id, item: reward_item(level, occupation) }; + let pickup = Pickup { interactor: player.id, item: reward_item(&SERVER.addons.events.legacy_koth, level, occupation) }; play_sound_at_player(player, sound::Kind::Missioncomplete, 0.62, 1.0).await; player.send_ignoring(&WorldUpdate::from(pickup)).await; } +#[derive(Debug, Clone, Copy)] +enum Loot { Weapon, Armor, Amulet, Ring, Leftovers, Spirit, Pet } + +#[derive(Debug, Deserialize)] +#[serde(default, deny_unknown_fields)] +struct LootWeights { weapon: u32, armor: u32, amulet: u32, ring: u32, leftovers: u32, spirit: u32, pet: u32 } + +impl Default for LootWeights { + fn default() -> Self { + Self { weapon: 25, armor: 25, amulet: 7, ring: 7, leftovers: 6, spirit: 25, pet: 5 } + } +} + +impl LootWeights { + const fn table(&self) -> [(Loot, u32); 7] { + [(Loot::Weapon, self.weapon), (Loot::Armor, self.armor), (Loot::Amulet, self.amulet), (Loot::Ring, self.ring), + (Loot::Leftovers, self.leftovers), (Loot::Spirit, self.spirit), (Loot::Pet, self.pet)] + } +} + +#[derive(Debug, Deserialize)] +#[serde(default, deny_unknown_fields)] +struct RarityWeights { normal: u32, uncommon: u32, rare: u32, epic: u32, legendary: u32 } + +impl Default for RarityWeights { + fn default() -> Self { + Self { normal: 0, uncommon: 0, rare: 0, epic: 1, legendary: 1 } + } +} + +impl RarityWeights { + const fn table(&self) -> [(u8, u32); 5] { + [(NORMAL, self.normal), (UNCOMMON, self.uncommon), (RARE, self.rare), (EPIC, self.epic), (LEGENDARY, self.legendary)] + } +} + +fn pick_weighted(table: &[(T, u32)]) -> Option { + table.choose_weighted(&mut rng(), |(_, weight)| *weight).ok().map(|(value, _)| *value) +} + type ClassWeapons = &'static [(kind::Weapon, &'static [Material])]; fn class_gear(occupation: Occupation) -> Option<(Material, ClassWeapons)> { @@ -219,27 +274,37 @@ fn class_gear(occupation: Occupation) -> Option<(Material, ClassWeapons)> { } } -fn reward_item(level: i16, occupation: Occupation) -> Item { +fn reward_item(lkoth: &LegacyKoth, level: i16, occupation: Occupation) -> Item { let mut item = Item::default(); - let roll = random::(); - (item.kind, item.material) = match class_gear(occupation) { - Some((_, weapons)) if roll < REWARD_WEAPON_ODDS => { + let gear = class_gear(occupation); + let table = lkoth.loot.table().map(|(loot, weight)| match loot { + Loot::Weapon | Loot::Armor if gear.is_none() => (loot, 0), + _ => (loot, weight) + }); + + (item.kind, item.material) = match (pick_weighted(&table), gear) { + (Some(Loot::Weapon), Some((_, weapons))) => { let (weapon, materials) = pick_from(weapons); (Kind::Weapon(weapon), pick_from(materials)) } - Some((armor, _)) if roll < REWARD_WEAPON_ODDS + REWARD_ARMOR_ODDS + (Some(Loot::Armor), Some((armor, _))) => (pick_from(&[Kind::Chest, Kind::Gloves, Kind::Boots, Kind::Shoulder]), armor), - _ => { - let kind = if roll < REWARD_WEAPON_ODDS + REWARD_ARMOR_ODDS + REWARD_SPIRIT_ODDS { Kind::Resource(kind::Resource::Spirit) } - else { Kind::Pet(pick_from(&Race::iter().collect::>())) }; + (loot, _) => { + let kind = match loot { + Some(Loot::Amulet) => Kind::Amulet, + Some(Loot::Ring) => Kind::Ring, + Some(Loot::Leftovers) => Kind::Leftovers, + Some(Loot::Pet) => Kind::Pet(pick_from(VANILLA_PETS)), + _ => Kind::Resource(kind::Resource::Spirit) + }; (kind, pick_from(by_item_kind(kind))) } }; item.rarity = match item.kind { - Kind::Weapon(_) | Kind::Chest | Kind::Gloves | Kind::Boots | Kind::Shoulder - => pick_from(&[EPIC, LEGENDARY]), + Kind::Weapon(_) | Kind::Chest | Kind::Gloves | Kind::Boots | Kind::Shoulder | Kind::Amulet | Kind::Ring | Kind::Leftovers + => pick_weighted(&lkoth.rarity.table()).expect("validated at startup"), Kind::Resource(kind::Resource::Spirit) => RARE, _ => if item.kind.uses_rarity() { LEGENDARY } else { NORMAL } }; From ba8a4ed398b5cbce0412322a7119e4263f83a00f Mon Sep 17 00:00:00 2001 From: Faye Date: Wed, 16 Sep 2026 13:59:31 +0200 Subject: [PATCH 3/4] add lamp to koth loot --- server/src/addon/events/legacy_koth.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/server/src/addon/events/legacy_koth.rs b/server/src/addon/events/legacy_koth.rs index e2fc575..6d61a1d 100644 --- a/server/src/addon/events/legacy_koth.rs +++ b/server/src/addon/events/legacy_koth.rs @@ -219,22 +219,22 @@ async fn give_reward(player: &Player) { } #[derive(Debug, Clone, Copy)] -enum Loot { Weapon, Armor, Amulet, Ring, Leftovers, Spirit, Pet } +enum Loot { Weapon, Armor, Amulet, Ring, Leftovers, Spirit, Lamp, Pet } #[derive(Debug, Deserialize)] #[serde(default, deny_unknown_fields)] -struct LootWeights { weapon: u32, armor: u32, amulet: u32, ring: u32, leftovers: u32, spirit: u32, pet: u32 } +struct LootWeights { weapon: u32, armor: u32, amulet: u32, ring: u32, leftovers: u32, spirit: u32, lamp: u32, pet: u32 } impl Default for LootWeights { fn default() -> Self { - Self { weapon: 25, armor: 25, amulet: 7, ring: 7, leftovers: 6, spirit: 25, pet: 5 } + Self { weapon: 25, armor: 25, amulet: 7, ring: 7, leftovers: 4, spirit: 25, lamp: 2, pet: 5 } } } impl LootWeights { - const fn table(&self) -> [(Loot, u32); 7] { + const fn table(&self) -> [(Loot, u32); 8] { [(Loot::Weapon, self.weapon), (Loot::Armor, self.armor), (Loot::Amulet, self.amulet), (Loot::Ring, self.ring), - (Loot::Leftovers, self.leftovers), (Loot::Spirit, self.spirit), (Loot::Pet, self.pet)] + (Loot::Leftovers, self.leftovers), (Loot::Spirit, self.spirit), (Loot::Lamp, self.lamp), (Loot::Pet, self.pet)] } } @@ -295,6 +295,7 @@ fn reward_item(lkoth: &LegacyKoth, level: i16, occupation: Occupation) -> Item { Some(Loot::Amulet) => Kind::Amulet, Some(Loot::Ring) => Kind::Ring, Some(Loot::Leftovers) => Kind::Leftovers, + Some(Loot::Lamp) => Kind::Lamp, Some(Loot::Pet) => Kind::Pet(pick_from(VANILLA_PETS)), _ => Kind::Resource(kind::Resource::Spirit) }; From bb78ab951912dbb58564ceb69d724bee85650fab Mon Sep 17 00:00:00 2001 From: Faye Date: Fri, 18 Sep 2026 17:09:40 +0200 Subject: [PATCH 4/4] add vanilla pets to protocol and other smaller fixes --- protocol/src/packet/common/item/kind.rs | 8 --- protocol/src/utils.rs | 8 +++ protocol/src/utils/constants.rs | 50 +++++++++++++++++++ .../addon/command_manager/commands/give.rs | 4 +- server/src/addon/events/legacy_koth.rs | 16 ++---- server/src/addon/events/shop.rs | 3 +- 6 files changed, 66 insertions(+), 23 deletions(-) diff --git a/protocol/src/packet/common/item/kind.rs b/protocol/src/packet/common/item/kind.rs index f69d629..44caab0 100644 --- a/protocol/src/packet/common/item/kind.rs +++ b/protocol/src/packet/common/item/kind.rs @@ -1,7 +1,6 @@ use strum_macros::*; use crate::packet::common::item::Kind; -use crate::utils::max_level_of; impl Kind { #[must_use] @@ -42,13 +41,6 @@ impl Kind { Self::Weapon(_) | Self::Chest | Self::Gloves | Self::Boots | Self::Shoulder | Self::Amulet | Self::Ring ) } - - #[must_use] - pub fn item_level(&self, player_level: i16) -> i16 { - if self.uses_power() { max_level_of(player_level.into()) as i16 } - else if self.uses_level() { player_level } - else { 1 } - } } #[repr(u8)] diff --git a/protocol/src/utils.rs b/protocol/src/utils.rs index e00640d..ea85d72 100644 --- a/protocol/src/utils.rs +++ b/protocol/src/utils.rs @@ -10,6 +10,7 @@ use tokio::io; use tokio::io::{AsyncRead, AsyncReadExt, AsyncWriteExt}; use crate::{ReadCwData, WriteCwData}; +use crate::packet::common::item::Kind; use crate::utils::constants::SIZE_BLOCK; use crate::utils::io_extensions::{ReadArbitrary, WriteArbitrary}; @@ -47,6 +48,13 @@ pub fn max_level_of(level: i32) -> i32 { (level..=i32::from(i16::MAX)).take_while(|&l| power_of(l) == power).last().unwrap_or(level) } +#[must_use] +pub fn max_valid_item_level(kind: Kind, player_level: i16) -> i16 { + if kind.uses_power() { max_level_of(player_level.into()) as i16 } + else if kind.uses_level() { player_level } + else { 1 } +} + #[must_use] pub fn maximum_experience_of(level: i32) -> i32 { // order of operations matches cubeworld's rounding behaviour diff --git a/protocol/src/utils/constants.rs b/protocol/src/utils/constants.rs index 5c118bd..d855c9e 100644 --- a/protocol/src/utils/constants.rs +++ b/protocol/src/utils/constants.rs @@ -63,6 +63,56 @@ pub const PLAYABLE_RACES: [Race; 16] = [ UndeadFemale ]; +pub const VANILLA_PETS: [Race; 47] = [ + Collie, + Alpaca, + AlpacaBrown, + Turtle, + Terrier, + TerrierScottish, + Cat, + Pig, + Sheep, + Bunny, + Porcupine, + SlimeGreen, + SlimePink, + SlimeYellow, + SlimeBlue, + Monkey, + Hornet, + Crow, + Chicken, + Seagull, + Parrot, + Bat, + Fly, + Midge, + Mosquito, + RunnerPlain, + RunnerLeaf, + RunnerSnow, + RunnerDesert, + Peacock, + Duckbill, + Crocodile, + Spitter, + Mole, + Biter, + Squirrel, + Raccoon, + Owl, + Penguin, + Horse, + Camel, + BeetleDark, + BeetleFire, + BeetleSnout, + BeetleLemon, + Crab, + Bumblebee +]; + pub const TWO_HANDED_WEAPONS: [Weapon; 10] = [ Longsword, Bow, diff --git a/server/src/addon/command_manager/commands/give.rs b/server/src/addon/command_manager/commands/give.rs index bcde72f..aded0fb 100644 --- a/server/src/addon/command_manager/commands/give.rs +++ b/server/src/addon/command_manager/commands/give.rs @@ -5,7 +5,7 @@ use protocol::packet::world_update::Pickup; use protocol::packet::WorldUpdate; use protocol::utils::constants::materials::by_item_kind; use protocol::utils::constants::rarity::*; -use protocol::utils::power_of; +use protocol::utils::{max_valid_item_level, power_of}; use tap::Pipe; use crate::addon::command_manager::{Command, CommandResult}; @@ -45,7 +45,7 @@ impl Command for super::Give { let valid_materials = by_item_kind(item.kind); if item.kind.uses_rarity() {item.rarity = LEGENDARY} else {item.rarity = NORMAL} - item.level = item.kind.item_level(caller.character.read().await.level as i16); + item.level = max_valid_item_level(item.kind, caller.character.read().await.level as i16); if !valid_materials.is_empty() {item.material = valid_materials[0]} for param in params { diff --git a/server/src/addon/events/legacy_koth.rs b/server/src/addon/events/legacy_koth.rs index 6d61a1d..e89204e 100644 --- a/server/src/addon/events/legacy_koth.rs +++ b/server/src/addon/events/legacy_koth.rs @@ -17,9 +17,10 @@ use protocol::packet::common::{CreatureId, EulerAngles, Hitbox, Item, Race, item use protocol::packet::creature_update::{Affiliation, Occupation}; use protocol::packet::world_update::{Mission, Pickup, sound}; use protocol::packet::world_update::mission::{Objective, State}; -use protocol::utils::constants::{SIZE_BLOCK, SIZE_ZONE, SIZE_SECTOR}; +use protocol::utils::constants::{SIZE_BLOCK, SIZE_ZONE, SIZE_SECTOR, VANILLA_PETS}; use protocol::utils::constants::materials::by_item_kind; use protocol::utils::constants::rarity::{NORMAL, UNCOMMON, RARE, EPIC, LEGENDARY}; +use protocol::utils::max_valid_item_level; use crate::addon::events::utils::{appearance_invisible, config_fallback, config_optional, creatures_circular, is_in_zone, pick_from, NAME_OVERFLOW, RENDER_DISTANCE_CREATURE}; use crate::addon::play_sound_at_player; @@ -34,15 +35,6 @@ const TORCHES_ID: i64 = 75000; const REWARD_POINTS: i32 = 10000; const REWARD_THRESHOLDS: [i32; 4] = [20, 40, 60, 80]; -const VANILLA_PETS: &[Race] = { - use Race::*; - &[Collie, Alpaca, AlpacaBrown, Turtle, Terrier, TerrierScottish, Cat, Pig, Sheep, Bunny, Porcupine, - SlimeGreen, SlimePink, SlimeYellow, SlimeBlue, Monkey, Hornet, Crow, Chicken, Seagull, Parrot, Bat, - Fly, Midge, Mosquito, RunnerPlain, RunnerLeaf, RunnerSnow, RunnerDesert, Peacock, Duckbill, Crocodile, - Spitter, Mole, Biter, Squirrel, Raccoon, Owl, Penguin, Horse, Camel, BeetleDark, BeetleFire, - BeetleSnout, BeetleLemon, Crab, Bumblebee] -}; - #[derive(Debug)] pub struct LegacyKoth { points: RwLock>, @@ -296,7 +288,7 @@ fn reward_item(lkoth: &LegacyKoth, level: i16, occupation: Occupation) -> Item { Some(Loot::Ring) => Kind::Ring, Some(Loot::Leftovers) => Kind::Leftovers, Some(Loot::Lamp) => Kind::Lamp, - Some(Loot::Pet) => Kind::Pet(pick_from(VANILLA_PETS)), + Some(Loot::Pet) => Kind::Pet(pick_from(&VANILLA_PETS)), _ => Kind::Resource(kind::Resource::Spirit) }; (kind, pick_from(by_item_kind(kind))) @@ -310,7 +302,7 @@ fn reward_item(lkoth: &LegacyKoth, level: i16, occupation: Occupation) -> Item { _ => if item.kind.uses_rarity() { LEGENDARY } else { NORMAL } }; - item.level = item.kind.item_level(level); + item.level = max_valid_item_level(item.kind, level); item.seed = if item.uses_seed() { random_range(0..=i32::MAX) } else { 0 }; item diff --git a/server/src/addon/events/shop.rs b/server/src/addon/events/shop.rs index 72b471a..4b19a3c 100644 --- a/server/src/addon/events/shop.rs +++ b/server/src/addon/events/shop.rs @@ -17,6 +17,7 @@ use protocol::packet::WorldUpdate; use protocol::packet::CreatureUpdate; use protocol::utils::constants::{materials, SIZE_BLOCK, SIZE_ZONE}; use protocol::utils::constants::rarity::*; +use protocol::utils::max_valid_item_level; use crate::addon::events::utils::{appearance_invisible, config_fallback, config_optional, creatures_circular, NAME_OVERFLOW}; use crate::addon::play_sound_at_player; @@ -305,7 +306,7 @@ fn item_validation(state: &mut State, item: &mut Item, player_level: i16) -> Res State::Rarity => {if item.kind.uses_rarity() { return Ok(()) } item.rarity = NORMAL; *state = State::Model;} - State::Model => {item.level = item.kind.item_level(player_level); + State::Model => {item.level = max_valid_item_level(item.kind, player_level); if item.uses_models() { return Ok(()) } item.seed = 0; *state = State::Stat;}