From 1cf66bf42a8f919f22861fa08f7f9584046074cf Mon Sep 17 00:00:00 2001 From: juliancoffee Date: Sun, 27 Jun 2021 14:59:34 +0300 Subject: [PATCH 01/12] bumb Biped Cultist armour --- .../items/npc_armor/biped_large/warlock.ron | 17 ++ .../items/npc_armor/biped_large/warlord.ron | 17 ++ common/src/comp/inventory/loadout_builder.rs | 145 ++++++++---------- 3 files changed, 99 insertions(+), 80 deletions(-) create mode 100644 assets/common/items/npc_armor/biped_large/warlock.ron create mode 100644 assets/common/items/npc_armor/biped_large/warlord.ron diff --git a/assets/common/items/npc_armor/biped_large/warlock.ron b/assets/common/items/npc_armor/biped_large/warlock.ron new file mode 100644 index 0000000000..c144ad8c1a --- /dev/null +++ b/assets/common/items/npc_armor/biped_large/warlock.ron @@ -0,0 +1,17 @@ +ItemDef( + name: "Giant Warlock Chest", + description: "Made of darkest silk.", + kind: Armor(( + kind: Chest("GiantWarlock"), + stats: ( + protection: Normal(250.0), + poise_resilience: Normal(1.0), + energy_max: 1000, + energy_reward: 1.0, + crit_power: 0.0, + stealth: 0.0, + ), + )), + quality: Moderate, + tags: [], +) diff --git a/assets/common/items/npc_armor/biped_large/warlord.ron b/assets/common/items/npc_armor/biped_large/warlord.ron new file mode 100644 index 0000000000..cb5bf1dbce --- /dev/null +++ b/assets/common/items/npc_armor/biped_large/warlord.ron @@ -0,0 +1,17 @@ +ItemDef( + name: "Giant Warlord Chest", + description: "Made of darkest steel.", + kind: Armor(( + kind: Chest("GiantWarlord"), + stats: ( + protection: Normal(300.0), + poise_resilience: Normal(1.0), + energy_max: 0, + energy_reward: 0.0, + crit_power: 0.0, + stealth: 0.0, + ), + )), + quality: Moderate, + tags: [], +) diff --git a/common/src/comp/inventory/loadout_builder.rs b/common/src/comp/inventory/loadout_builder.rs index 129a8d2b5e..28def5a7e1 100644 --- a/common/src/comp/inventory/loadout_builder.rs +++ b/common/src/comp/inventory/loadout_builder.rs @@ -398,88 +398,73 @@ impl LoadoutBuilder { #[must_use] /// Set default equipement based on `body` - pub fn with_default_equipment(mut self, body: &Body) -> Self { - self = match body { - Body::BipedLarge(biped_large::Body { - species: biped_large::Species::Mindflayer, - .. - }) => self.chest(Some(Item::new_from_asset_expect( - "common.items.npc_armor.biped_large.mindflayer", - ))), - Body::BipedLarge(biped_large::Body { - species: biped_large::Species::Minotaur, - .. - }) => self.chest(Some(Item::new_from_asset_expect( - "common.items.npc_armor.biped_large.minotaur", - ))), - Body::BipedLarge(biped_large::Body { - species: biped_large::Species::Tidalwarrior, - .. - }) => self.chest(Some(Item::new_from_asset_expect( - "common.items.npc_armor.biped_large.tidal_warrior", - ))), - Body::BipedLarge(biped_large::Body { - species: biped_large::Species::Yeti, - .. - }) => self.chest(Some(Item::new_from_asset_expect( - "common.items.npc_armor.biped_large.yeti", - ))), - Body::BipedLarge(biped_large::Body { - species: biped_large::Species::Harvester, - .. - }) => self.chest(Some(Item::new_from_asset_expect( - "common.items.npc_armor.biped_large.harvester", - ))), - Body::BipedLarge(biped_large::Body { - species: - biped_large::Species::Ogre - | biped_large::Species::Cyclops - | biped_large::Species::Blueoni - | biped_large::Species::Redoni - | biped_large::Species::Cavetroll - | biped_large::Species::Wendigo, - .. - }) => self.chest(Some(Item::new_from_asset_expect( - "common.items.npc_armor.biped_large.generic", - ))), - Body::Golem(golem::Body { - species: golem::Species::ClayGolem, - .. - }) => self.chest(Some(Item::new_from_asset_expect( - "common.items.npc_armor.golem.claygolem", - ))), - Body::QuadrupedLow(quadruped_low::Body { - species: - quadruped_low::Species::Basilisk - | quadruped_low::Species::Asp - | quadruped_low::Species::Lavadrake - | quadruped_low::Species::Maneater - | quadruped_low::Species::Rocksnapper - | quadruped_low::Species::Sandshark, - .. - }) => self.chest(Some(Item::new_from_asset_expect( - "common.items.npc_armor.quadruped_low.generic", - ))), - Body::QuadrupedLow(quadruped_low::Body { - species: quadruped_low::Species::Tortoise, - .. - }) => self.chest(Some(Item::new_from_asset_expect( - "common.items.npc_armor.quadruped_low.shell", - ))), - Body::Theropod(theropod::Body { - species: - theropod::Species::Archaeos - | theropod::Species::Yale - | theropod::Species::Ntouka - | theropod::Species::Odonto, - .. - }) => self.chest(Some(Item::new_from_asset_expect( - "common.items.npc_armor.theropod.rugged", - ))), - _ => self, + pub fn with_default_equipment(self, body: &Body) -> Self { + let chest = match body { + Body::BipedLarge(body) => match body.species { + biped_large::Species::Mindflayer => { + Some("common.items.npc_armor.biped_large.mindflayer") + }, + biped_large::Species::Minotaur => { + Some("common.items.npc_armor.biped_large.minotaur") + }, + biped_large::Species::Tidalwarrior => { + Some("common.items.npc_armor.biped_large.tidal_warrior") + }, + biped_large::Species::Yeti => Some("common.items.npc_armor.biped_large.yeti"), + biped_large::Species::Harvester => { + Some("common.items.npc_armor.biped_large.harvester") + }, + biped_large::Species::Ogre + | biped_large::Species::Cyclops + | biped_large::Species::Blueoni + | biped_large::Species::Redoni + | biped_large::Species::Cavetroll + | biped_large::Species::Wendigo => { + Some("common.items.npc_armor.biped_large.generic") + }, + biped_large::Species::Cultistwarlord => { + Some("common.items.npc_armor.biped_large.warlord") + }, + biped_large::Species::Cultistwarlock => { + Some("common.items.npc_armor.biped_large.warlock") + }, + _ => None, + }, + Body::Golem(body) => match body.species { + golem::Species::ClayGolem => Some("common.items.npc_armor.golem.claygolem"), + _ => None, + }, + Body::QuadrupedLow(body) => match body.species { + quadruped_low::Species::Basilisk + | quadruped_low::Species::Asp + | quadruped_low::Species::Lavadrake + | quadruped_low::Species::Maneater + | quadruped_low::Species::Rocksnapper + | quadruped_low::Species::Sandshark => { + Some("common.items.npc_armor.quadruped_low.generic") + }, + quadruped_low::Species::Tortoise => { + Some("common.items.npc_armor.quadruped_low.shell") + }, + _ => None, + }, + Body::Theropod(body) => match body.species { + theropod::Species::Archaeos + | theropod::Species::Yale + | theropod::Species::Ntouka + | theropod::Species::Odonto => Some("common.items.npc_armor.theropod.rugged"), + _ => None, + }, + _ => None, }; - self + // closures can't be used here, because it moves value + #[allow(clippy::option_if_let_else)] + if let Some(chest) = chest { + self.chest(Some(Item::new_from_asset_expect(chest))) + } else { + self + } } #[must_use] From e95539a77a415fbf1efdbde9918daedd3f214424 Mon Sep 17 00:00:00 2001 From: juliancoffee Date: Sun, 27 Jun 2021 15:12:10 +0300 Subject: [PATCH 02/12] Make cultist great again - Change cultist set stats to mixed dragonscale (hands and feet), sunsilk (belt, pants and shoulders) and orichalcum (chest and cape) Distribute cultist set stats as hybrid set. - Make cultist cape count towards npc aggro - Remove unused staff skilltree from cultist - Remove spin number skill from sword cultist skillset --- assets/common/items/armor/cultist/belt.ron | 12 +++++------ assets/common/items/armor/cultist/chest.ron | 12 +++++------ assets/common/items/armor/cultist/foot.ron | 12 +++++------ assets/common/items/armor/cultist/hand.ron | 12 +++++------ assets/common/items/armor/cultist/pants.ron | 12 +++++------ .../common/items/armor/cultist/shoulder.ron | 12 +++++------ .../items/armor/misc/back/dungeon_purple.ron | 16 +++++++------- .../common/skillset/dungeon/tier-5/enemy.ron | 1 - .../common/skillset/dungeon/tier-5/staff.ron | 21 ------------------- .../common/skillset/dungeon/tier-5/sword.ron | 5 ++--- assets/server/manifests/kits.ron | 9 ++++++++ 11 files changed, 56 insertions(+), 68 deletions(-) delete mode 100644 assets/common/skillset/dungeon/tier-5/staff.ron diff --git a/assets/common/items/armor/cultist/belt.ron b/assets/common/items/armor/cultist/belt.ron index 55d4758882..89619d4ac0 100644 --- a/assets/common/items/armor/cultist/belt.ron +++ b/assets/common/items/armor/cultist/belt.ron @@ -4,11 +4,11 @@ ItemDef( kind: Armor(( kind: Belt("Cultist"), stats: ( - protection: Normal(6.0), - poise_resilience: Normal(0.0), - energy_max: 0, - energy_reward: 0.0, - crit_power: 0.0, + protection: Normal(8.0), + poise_resilience: Normal(1.0), + energy_max: 20, + energy_reward: 0.025, + crit_power: 0.02, stealth: 0.0, ), )), @@ -16,4 +16,4 @@ ItemDef( tags: [ Cultist, ], -) \ No newline at end of file +) diff --git a/assets/common/items/armor/cultist/chest.ron b/assets/common/items/armor/cultist/chest.ron index acfc5dac59..a20727ed72 100644 --- a/assets/common/items/armor/cultist/chest.ron +++ b/assets/common/items/armor/cultist/chest.ron @@ -4,11 +4,11 @@ ItemDef( kind: Armor(( kind: Chest("Cultist"), stats: ( - protection: Normal(30.0), - poise_resilience: Normal(0.0), - energy_max: 0, - energy_reward: 0.0, - crit_power: 0.0, + protection: Normal(48.0), + poise_resilience: Normal(6.0), + energy_max: 135, + energy_reward: 0.135, + crit_power: 0.125, stealth: 0.0, ), )), @@ -16,4 +16,4 @@ ItemDef( tags: [ Cultist, ], -) \ No newline at end of file +) diff --git a/assets/common/items/armor/cultist/foot.ron b/assets/common/items/armor/cultist/foot.ron index 41f4f572c7..0e396bf4f1 100644 --- a/assets/common/items/armor/cultist/foot.ron +++ b/assets/common/items/armor/cultist/foot.ron @@ -4,11 +4,11 @@ ItemDef( kind: Armor(( kind: Foot("Cultist"), stats: ( - protection: Normal(6.0), - poise_resilience: Normal(0.0), - energy_max: 0, - energy_reward: 0.0, - crit_power: 0.0, + protection: Normal(16.0), + poise_resilience: Normal(2.0), + energy_max: 45, + energy_reward: 0.045, + crit_power: 0.04, stealth: 0.0, ), )), @@ -16,4 +16,4 @@ ItemDef( tags: [ Cultist, ], -) \ No newline at end of file +) diff --git a/assets/common/items/armor/cultist/hand.ron b/assets/common/items/armor/cultist/hand.ron index 4b5939dafb..f9e140b28f 100644 --- a/assets/common/items/armor/cultist/hand.ron +++ b/assets/common/items/armor/cultist/hand.ron @@ -4,11 +4,11 @@ ItemDef( kind: Armor(( kind: Hand("Cultist"), stats: ( - protection: Normal(12.0), - poise_resilience: Normal(0.0), - energy_max: 0, - energy_reward: 0.0, - crit_power: 0.0, + protection: Normal(16.0), + poise_resilience: Normal(2.0), + energy_max: 45, + energy_reward: 0.045, + crit_power: 0.04, stealth: 0.0, ), )), @@ -16,4 +16,4 @@ ItemDef( tags: [ Cultist, ], -) \ No newline at end of file +) diff --git a/assets/common/items/armor/cultist/pants.ron b/assets/common/items/armor/cultist/pants.ron index 8461be9c53..022ce3e037 100644 --- a/assets/common/items/armor/cultist/pants.ron +++ b/assets/common/items/armor/cultist/pants.ron @@ -4,11 +4,11 @@ ItemDef( kind: Armor(( kind: Pants("Cultist"), stats: ( - protection: Normal(24.0), - poise_resilience: Normal(0.0), - energy_max: 0, - energy_reward: 0.0, - crit_power: 0.0, + protection: Normal(32.0), + poise_resilience: Normal(4.0), + energy_max: 90, + energy_reward: 0.1, + crit_power: 0.08, stealth: 0.0, ), )), @@ -16,4 +16,4 @@ ItemDef( tags: [ Cultist, ], -) \ No newline at end of file +) diff --git a/assets/common/items/armor/cultist/shoulder.ron b/assets/common/items/armor/cultist/shoulder.ron index d88bd22f51..8aa550f853 100644 --- a/assets/common/items/armor/cultist/shoulder.ron +++ b/assets/common/items/armor/cultist/shoulder.ron @@ -4,11 +4,11 @@ ItemDef( kind: Armor(( kind: Shoulder("Cultist"), stats: ( - protection: Normal(18.0), - poise_resilience: Normal(0.0), - energy_max: 0, - energy_reward: 0.0, - crit_power: 0.0, + protection: Normal(32.0), + poise_resilience: Normal(5.0), + energy_max: 90, + energy_reward: 0.1, + crit_power: 0.08, stealth: 0.0, ), )), @@ -16,4 +16,4 @@ ItemDef( tags: [ Cultist, ], -) \ No newline at end of file +) diff --git a/assets/common/items/armor/misc/back/dungeon_purple.ron b/assets/common/items/armor/misc/back/dungeon_purple.ron index e143979c07..7f7a7dd305 100644 --- a/assets/common/items/armor/misc/back/dungeon_purple.ron +++ b/assets/common/items/armor/misc/back/dungeon_purple.ron @@ -4,14 +4,16 @@ ItemDef( kind: Armor(( kind: Back("DungeonPurple"), stats: ( - protection: Normal(3.0), - poise_resilience: Normal(0.0), - energy_max: 0, - energy_reward: 0.0, - crit_power: 0.0, + protection: Normal(8.0), + poise_resilience: Normal(1.0), + energy_max: 20, + energy_reward: 0.025, + crit_power: 0.02, stealth: 0.0, ), )), quality: Epic, - tags: [], -) \ No newline at end of file + tags: [ + Cultist, + ], +) diff --git a/assets/common/skillset/dungeon/tier-5/enemy.ron b/assets/common/skillset/dungeon/tier-5/enemy.ron index 0e5ff1c8e5..d0ee8c79f6 100644 --- a/assets/common/skillset/dungeon/tier-5/enemy.ron +++ b/assets/common/skillset/dungeon/tier-5/enemy.ron @@ -4,5 +4,4 @@ Tree("common.skillset.dungeon.tier-5.axe"), Tree("common.skillset.dungeon.tier-5.hammer"), Tree("common.skillset.dungeon.tier-5.bow"), - Tree("common.skillset.dungeon.tier-5.staff"), ]) diff --git a/assets/common/skillset/dungeon/tier-5/staff.ron b/assets/common/skillset/dungeon/tier-5/staff.ron deleted file mode 100644 index 4c3dc1af3a..0000000000 --- a/assets/common/skillset/dungeon/tier-5/staff.ron +++ /dev/null @@ -1,21 +0,0 @@ -([ - Group(Weapon(Staff)), - - // Fireball - Skill((Staff(BDamage), Some(1))), - Skill((Staff(BRegen), Some(1))), - Skill((Staff(BRadius), Some(1))), - - // Flamethrower - Skill((Staff(FRange), Some(1))), - Skill((Staff(FDrain), Some(1))), - Skill((Staff(FDamage), Some(1))), - Skill((Staff(FVelocity), Some(1))), - - // Shockwave - Skill((Staff(UnlockShockwave), None)), - Skill((Staff(SDamage), Some(1))), - Skill((Staff(SKnockback), Some(1))), - Skill((Staff(SRange), Some(1))), - Skill((Staff(SCost), Some(1))), -]) diff --git a/assets/common/skillset/dungeon/tier-5/sword.ron b/assets/common/skillset/dungeon/tier-5/sword.ron index 2f945fc42f..653b99296f 100644 --- a/assets/common/skillset/dungeon/tier-5/sword.ron +++ b/assets/common/skillset/dungeon/tier-5/sword.ron @@ -13,7 +13,6 @@ // Spin of death Skill((Sword(UnlockSpin), None)), - Skill((Sword(SDamage), Some(1))), - Skill((Sword(SSpins), Some(2))), - Skill((Sword(SCost), Some(1))), + Skill((Sword(SDamage), Some(2))), + Skill((Sword(SCost), Some(2))), ]) diff --git a/assets/server/manifests/kits.ron b/assets/server/manifests/kits.ron index 1c1e45d206..2b0c8b0da2 100644 --- a/assets/server/manifests/kits.ron +++ b/assets/server/manifests/kits.ron @@ -314,6 +314,15 @@ ("common.items.armor.ferocious.back",1), ("common.items.weapons.sword.bloodsteel-1",1), ], + "cultist": [ + ("common.items.armor.cultist.chest",1), + ("common.items.armor.cultist.pants",1), + ("common.items.armor.cultist.hand",1), + ("common.items.armor.cultist.foot",1), + ("common.items.armor.cultist.shoulder",1), + ("common.items.armor.cultist.belt",1), + ("common.items.armor.misc.back.dungeon_purple",1), + ], "admin_cosmetics": [ ("common.items.debug.cultist_belt",1), ("common.items.debug.cultist_boots",1), From d929d454d1680df20a4339e5a9371ba6c43d0007 Mon Sep 17 00:00:00 2001 From: juliancoffee Date: Sun, 27 Jun 2021 16:26:41 +0300 Subject: [PATCH 03/12] Make QuadMed great again - Make Bonerattler, Darkhound, Lion, Snowleopard use QuadMedJump (Saber attack) --- common/src/comp/inventory/loadout_builder.rs | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/common/src/comp/inventory/loadout_builder.rs b/common/src/comp/inventory/loadout_builder.rs index 28def5a7e1..4c500dff97 100644 --- a/common/src/comp/inventory/loadout_builder.rs +++ b/common/src/comp/inventory/loadout_builder.rs @@ -161,14 +161,9 @@ fn default_main_tool(body: &Body) -> Item { _ => None, }, Body::QuadrupedMedium(quadruped_medium) => match quadruped_medium.species { - quadruped_medium::Species::Wolf - | quadruped_medium::Species::Grolgar - | quadruped_medium::Species::Lion - | quadruped_medium::Species::Bonerattler - | quadruped_medium::Species::Darkhound - | quadruped_medium::Species::Snowleopard => Some(Item::new_from_asset_expect( - "common.items.npc_weapons.unique.quadmedquick", - )), + quadruped_medium::Species::Wolf | quadruped_medium::Species::Grolgar => Some( + Item::new_from_asset_expect("common.items.npc_weapons.unique.quadmedquick"), + ), quadruped_medium::Species::Donkey | quadruped_medium::Species::Horse | quadruped_medium::Species::Zebra @@ -180,7 +175,11 @@ fn default_main_tool(body: &Body) -> Item { | quadruped_medium::Species::Alpaca => Some(Item::new_from_asset_expect( "common.items.npc_weapons.unique.quadmedhoof", )), - quadruped_medium::Species::Saber => Some(Item::new_from_asset_expect( + quadruped_medium::Species::Saber + | quadruped_medium::Species::Bonerattler + | quadruped_medium::Species::Darkhound + | quadruped_medium::Species::Lion + | quadruped_medium::Species::Snowleopard => Some(Item::new_from_asset_expect( "common.items.npc_weapons.unique.quadmedjump", )), quadruped_medium::Species::Tuskram From 9eb1ae8b9128b2b0d082ca7984d41c4a7be05ed8 Mon Sep 17 00:00:00 2001 From: juliancoffee Date: Sun, 27 Jun 2021 16:27:31 +0300 Subject: [PATCH 04/12] Nerf Husk Brute - Remove HuskBrute health level buff - Make their attack slower --- .../common/abilities/custom/husk_brute/singlestrike.ron | 8 ++++---- assets/common/items/npc_weapons/unique/husk_brute.ron | 2 +- common/src/comp/body.rs | 1 + 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/assets/common/abilities/custom/husk_brute/singlestrike.ron b/assets/common/abilities/custom/husk_brute/singlestrike.ron index 3c93be79ca..ce9cc97599 100644 --- a/assets/common/abilities/custom/husk_brute/singlestrike.ron +++ b/assets/common/abilities/custom/husk_brute/singlestrike.ron @@ -2,17 +2,17 @@ ComboMelee( stage_data: [ ( stage: 1, - base_damage: 160, + base_damage: 200, damage_increase: 0, base_poise_damage: 12, poise_damage_increase: 0, knockback: 5.0, range: 3.5, angle: 60.0, - base_buildup_duration: 0.25, - base_swing_duration: 0.07, + base_buildup_duration: 0.5, + base_swing_duration: 0.2, hit_timing: 0.5, - base_recover_duration: 0.25, + base_recover_duration: 0.5, forward_movement: 0.5, damage_kind: Crushing, ), diff --git a/assets/common/items/npc_weapons/unique/husk_brute.ron b/assets/common/items/npc_weapons/unique/husk_brute.ron index c2389e824c..3766fba143 100644 --- a/assets/common/items/npc_weapons/unique/husk_brute.ron +++ b/assets/common/items/npc_weapons/unique/husk_brute.ron @@ -15,4 +15,4 @@ ItemDef( quality: Low, tags: [], ability_spec: Some(Custom("Husk Brute")), -) \ No newline at end of file +) diff --git a/common/src/comp/body.rs b/common/src/comp/body.rs index 2ae9bc6905..9925e692c9 100644 --- a/common/src/comp/body.rs +++ b/common/src/comp/body.rs @@ -625,6 +625,7 @@ impl Body { biped_large::Species::Dullahan => 120, biped_large::Species::Huskbrute => 100, // Boss enemies have their health set, not adjusted by level. + biped_large::Species::Huskbrute => 0, biped_large::Species::Mindflayer => 0, biped_large::Species::Minotaur => 0, biped_large::Species::Tidalwarrior => 0, From ed37a6f2f362f0b8fba55ce52b674edee0832225 Mon Sep 17 00:00:00 2001 From: juliancoffee Date: Sun, 27 Jun 2021 22:12:41 +0300 Subject: [PATCH 05/12] Slower ori_rate of StaffSimple flamethrower + use FromOri for StaffSimple flamethrower --- assets/common/abilities/staffsimple/flamethrower.ron | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/assets/common/abilities/staffsimple/flamethrower.ron b/assets/common/abilities/staffsimple/flamethrower.ron index 82cedf954f..b024bae2e4 100644 --- a/assets/common/abilities/staffsimple/flamethrower.ron +++ b/assets/common/abilities/staffsimple/flamethrower.ron @@ -14,7 +14,7 @@ BasicBeam( ))), energy_regen: 0, energy_drain: 350, - orientation_behavior: Normal, - ori_rate: 0.6, + orientation_behavior: FromOri, + ori_rate: 0.3, specifier: Flamethrower, ) From d339fc4343cfac49eae6d2c21ba88b4345d3cd93 Mon Sep 17 00:00:00 2001 From: juliancoffee Date: Sun, 27 Jun 2021 18:13:18 +0300 Subject: [PATCH 06/12] More deterministic dungeon fights - Spawn fixed number of enemies (2-4) in a room with any size. 1-3 enemies for T5 dungeon. --- world/src/site2/plot/dungeon.rs | 442 +++++++++++++++++++------------- 1 file changed, 258 insertions(+), 184 deletions(-) diff --git a/world/src/site2/plot/dungeon.rs b/world/src/site2/plot/dungeon.rs index 43cf4f6917..a4780c7634 100644 --- a/world/src/site2/plot/dungeon.rs +++ b/world/src/site2/plot/dungeon.rs @@ -187,7 +187,7 @@ impl Tile { pub struct Room { seed: u32, loot_density: f32, - enemy_density: Option, + fight: bool, miniboss: bool, boss: bool, area: Rect, @@ -252,7 +252,7 @@ impl Floor { let upstair_room = this.create_room(Room { seed: ctx.rng.gen(), loot_density: 0.0, - enemy_density: None, + fight: false, miniboss: false, boss: false, area: Rect::from((stair_tile - tile_offset - 1, Extent2::broadcast(3))), @@ -265,7 +265,8 @@ impl Floor { this.create_room(Room { seed: ctx.rng.gen(), loot_density: 0.0, - enemy_density: Some((0.0002 * difficulty as f32).min(0.001)), // Minions! + // Our bosses are intelligent enough to summon minions if needed. + fight: false, miniboss: false, boss: true, area: Rect::from(( @@ -281,7 +282,7 @@ impl Floor { let downstair_room = this.create_room(Room { seed: ctx.rng.gen(), loot_density: 0.0, - enemy_density: None, + fight: false, miniboss: false, boss: false, area: Rect::from((new_stair_tile - tile_offset - 1, Extent2::broadcast(3))), @@ -358,10 +359,11 @@ impl Floor { let mut dynamic_rng = rand::thread_rng(); match dynamic_rng.gen_range(0..5) { + // Miniboss room 0 => self.create_room(Room { seed: ctx.rng.gen(), loot_density: 0.000025 + level as f32 * 0.00015, - enemy_density: None, + fight: false, miniboss: true, boss: false, area, @@ -369,10 +371,11 @@ impl Floor { pillars: Some(ctx.rng.gen_range(2..=4)), difficulty: self.difficulty, }), + // Fight room with enemies in it _ => self.create_room(Room { seed: ctx.rng.gen(), loot_density: 0.000025 + level as f32 * 0.00015, - enemy_density: Some(0.001 + level as f32 * 0.00006), + fight: true, miniboss: false, boss: false, area, @@ -433,7 +436,6 @@ impl Floor { } } - #[allow(clippy::match_single_binding)] // TODO: Pending review in #587 fn apply_supplement( &self, // NOTE: Used only for dynamic elements like chests and entities! @@ -477,79 +479,108 @@ impl Floor { .map(|e| e.div_euclid(TILE_SIZE) * TILE_SIZE + TILE_SIZE / 2), ); - let tile_is_pillar = room - .pillars - .map(|pillar_space| { - tile_pos + // Fill regular rooms + if room.fight { + let enemy_spawn_tile = room.area.center(); + // Don't spawn enemies in a pillar + let enemy_tile_is_pillar = room.pillars.map_or(false, |pillar_space| { + enemy_spawn_tile .map(|e| e.rem_euclid(pillar_space) == 0) .reduce_and() - }) - .unwrap_or(false); + }); + let enemy_spawn_tile = + enemy_spawn_tile + if enemy_tile_is_pillar { 1 } else { 0 }; - if room - .enemy_density - .map(|density| dynamic_rng.gen_range(0..density.recip() as usize) == 0) - .unwrap_or(false) - && !tile_is_pillar - && !room.boss - { - // Randomly displace them a little - let raw_entity = EntityInfo::at( - tile_wcenter.map(|e| e as f32) - + Vec3::::iota() - .map(|e| { - (RandomField::new(room.seed.wrapping_add(10 + e)) - .get(Vec3::from(tile_pos)) - % 32) as i32 - - 16 - }) - .map(|e| e as f32 / 16.0), - ); + // Toss mobs in the center of the room + if tile_pos == enemy_spawn_tile && wpos2d == tile_wcenter.xy() { + let entities = match room.difficulty { + 0 => enemy_0(dynamic_rng, tile_wcenter), + 1 => enemy_1(dynamic_rng, tile_wcenter), + 2 => enemy_2(dynamic_rng, tile_wcenter), + 3 => enemy_3(dynamic_rng, tile_wcenter), + 4 => enemy_4(dynamic_rng, tile_wcenter), + 5 => enemy_5(dynamic_rng, tile_wcenter), + _ => enemy_fallback(dynamic_rng, tile_wcenter), + }; - let entity = match room.difficulty { - 0 => enemy_0(dynamic_rng, raw_entity), - 1 => enemy_1(dynamic_rng, raw_entity), - 2 => enemy_2(dynamic_rng, raw_entity), - 3 => enemy_3(dynamic_rng, raw_entity), - 4 => enemy_4(dynamic_rng, raw_entity), - 5 => enemy_5(dynamic_rng, raw_entity), - _ => enemy_fallback(raw_entity), - }; - supplement.add_entity( - entity.with_alignment(comp::Alignment::Enemy).with_level( - dynamic_rng - .gen_range( - (room.difficulty as f32).powf(1.25) + 3.0 - ..(room.difficulty as f32).powf(1.5) + 4.0, - ) - .round() as u16, - ), - ); + for entity in entities { + supplement.add_entity( + entity + .with_level( + dynamic_rng + .gen_range( + (room.difficulty as f32).powf(1.25) + 3.0 + ..(room.difficulty as f32).powf(1.5) + 4.0, + ) + .round() + as u16, + ) + .with_alignment(comp::Alignment::Enemy), + ); + } + } else { + // Turrets + if dynamic_rng.gen_range(0..5000) == 0 { + let pos = tile_wcenter.map(|e| e as f32) + + Vec3::::iota() + .map(|e| { + (RandomField::new(room.seed.wrapping_add(10 + e)) + .get(Vec3::from(tile_pos)) + % 32) + as i32 + - 16 + }) + .map(|e| e as f32 / 16.0); + let turret = EntityInfo::at(pos.map(|e| e as f32)) + .with_alignment(comp::Alignment::Enemy); + match room.difficulty { + 3 => { + let turret = turret + .with_body(comp::Body::Object( + comp::object::Body::Crossbow, + )) + .with_asset_expect( + "common.entity.dungeon.tier-3.sentry", + ); + supplement.add_entity(turret); + }, + 5 => { + let turret = turret + .with_body(comp::Body::Object( + comp::object::Body::Crossbow, + )) + .with_asset_expect( + "common.entity.dungeon.tier-5.turret", + ); + supplement.add_entity(turret); + }, + _ => {}, + }; + } + } } - if room.boss { - let boss_spawn_tile = room.area.center(); - // Don't spawn the boss in a pillar - let boss_tile_is_pillar = room - .pillars - .map(|pillar_space| { - boss_spawn_tile - .map(|e| e.rem_euclid(pillar_space) == 0) - .reduce_and() - }) - .unwrap_or(false); - let boss_spawn_tile = - boss_spawn_tile + if boss_tile_is_pillar { 1 } else { 0 }; + // Spawn miniboss (or minibosses) + if room.miniboss { + let miniboss_spawn_tile = room.area.center(); + // Don't spawn the miniboss in a pillar + let miniboss_tile_is_pillar = room.pillars.map_or(false, |pillar_space| { + miniboss_spawn_tile + .map(|e| e.rem_euclid(pillar_space) == 0) + .reduce_and() + }); + let miniboss_spawn_tile = + miniboss_spawn_tile + if miniboss_tile_is_pillar { 1 } else { 0 }; - if tile_pos == boss_spawn_tile && tile_wcenter.xy() == wpos2d { + if tile_pos == miniboss_spawn_tile && tile_wcenter.xy() == wpos2d { let entities = match room.difficulty { - 0 => boss_0(tile_wcenter), - 1 => boss_1(tile_wcenter), - 2 => boss_2(tile_wcenter), - 3 => boss_3(tile_wcenter), - 4 => boss_4(tile_wcenter), - 5 => boss_5(tile_wcenter), - _ => boss_fallback(tile_wcenter), + 0 => mini_boss_0(tile_wcenter), + 1 => mini_boss_1(tile_wcenter), + 2 => mini_boss_2(tile_wcenter), + 3 => mini_boss_3(tile_wcenter), + 4 => mini_boss_4(tile_wcenter), + 5 => mini_boss_5(dynamic_rng, tile_wcenter), + _ => mini_boss_fallback(tile_wcenter), }; for entity in entities { @@ -570,29 +601,28 @@ impl Floor { } } } - if room.miniboss { - let miniboss_spawn_tile = room.area.center(); - // Don't spawn the miniboss in a pillar - let miniboss_tile_is_pillar = room - .pillars - .map(|pillar_space| { - miniboss_spawn_tile - .map(|e| e.rem_euclid(pillar_space) == 0) - .reduce_and() - }) - .unwrap_or(false); - let miniboss_spawn_tile = - miniboss_spawn_tile + if miniboss_tile_is_pillar { 1 } else { 0 }; - if tile_pos == miniboss_spawn_tile && tile_wcenter.xy() == wpos2d { + // Spawn miniboss + if room.boss { + let boss_spawn_tile = room.area.center(); + // Don't spawn the boss in a pillar + let boss_tile_is_pillar = room.pillars.map_or(false, |pillar_space| { + boss_spawn_tile + .map(|e| e.rem_euclid(pillar_space) == 0) + .reduce_and() + }); + let boss_spawn_tile = + boss_spawn_tile + if boss_tile_is_pillar { 1 } else { 0 }; + + if tile_pos == boss_spawn_tile && wpos2d == tile_wcenter.xy() { let entities = match room.difficulty { - 0 => mini_boss_0(tile_wcenter), - 1 => mini_boss_1(tile_wcenter), - 2 => mini_boss_2(tile_wcenter), - 3 => mini_boss_3(tile_wcenter), - 4 => mini_boss_4(tile_wcenter), - 5 => mini_boss_5(dynamic_rng, tile_wcenter), - _ => mini_boss_fallback(tile_wcenter), + 0 => boss_0(tile_wcenter), + 1 => boss_1(tile_wcenter), + 2 => boss_2(tile_wcenter), + 3 => boss_3(tile_wcenter), + 4 => boss_4(tile_wcenter), + 5 => boss_5(tile_wcenter), + _ => boss_fallback(tile_wcenter), }; for entity in entities { @@ -631,61 +661,105 @@ impl Floor { } } -fn enemy_0(dynamic_rng: &mut impl Rng, entity: EntityInfo) -> EntityInfo { - match dynamic_rng.gen_range(0..5) { - 0 => entity.with_asset_expect("common.entity.dungeon.tier-0.bow"), - 1 => entity.with_asset_expect("common.entity.dungeon.tier-0.staff"), - _ => entity.with_asset_expect("common.entity.dungeon.tier-0.spear"), - } +fn enemy_0(dynamic_rng: &mut impl Rng, tile_wcenter: Vec3) -> Vec { + let number = dynamic_rng.gen_range(2..=4); + let mut entities = Vec::new(); + entities.resize_with(number, || { + let entity = EntityInfo::at(tile_wcenter.map(|e| e as f32)); + match dynamic_rng.gen_range(0..=4) { + 0 => entity.with_asset_expect("common.entity.dungeon.tier-0.bow"), + 1 => entity.with_asset_expect("common.entity.dungeon.tier-0.staff"), + _ => entity.with_asset_expect("common.entity.dungeon.tier-0.spear"), + } + }); + + entities } -fn enemy_1(dynamic_rng: &mut impl Rng, entity: EntityInfo) -> EntityInfo { - match dynamic_rng.gen_range(0..5) { - 0 => entity.with_asset_expect("common.entity.dungeon.tier-1.bow"), - 1 => entity.with_asset_expect("common.entity.dungeon.tier-1.staff"), - _ => entity.with_asset_expect("common.entity.dungeon.tier-1.spear"), - } +fn enemy_1(dynamic_rng: &mut impl Rng, tile_wcenter: Vec3) -> Vec { + let number = dynamic_rng.gen_range(2..=4); + let mut entities = Vec::new(); + entities.resize_with(number, || { + let entity = EntityInfo::at(tile_wcenter.map(|e| e as f32)); + match dynamic_rng.gen_range(0..=4) { + 0 => entity.with_asset_expect("common.entity.dungeon.tier-1.bow"), + 1 => entity.with_asset_expect("common.entity.dungeon.tier-1.staff"), + _ => entity.with_asset_expect("common.entity.dungeon.tier-1.spear"), + } + }); + + entities } -fn enemy_2(dynamic_rng: &mut impl Rng, entity: EntityInfo) -> EntityInfo { - match dynamic_rng.gen_range(0..5) { - 0 => entity.with_asset_expect("common.entity.dungeon.tier-2.bow"), - 1 => entity.with_asset_expect("common.entity.dungeon.tier-2.staff"), - _ => entity.with_asset_expect("common.entity.dungeon.tier-2.spear"), - } +fn enemy_2(dynamic_rng: &mut impl Rng, tile_wcenter: Vec3) -> Vec { + let number = dynamic_rng.gen_range(2..=4); + let mut entities = Vec::new(); + entities.resize_with(number, || { + let entity = EntityInfo::at(tile_wcenter.map(|e| e as f32)); + match dynamic_rng.gen_range(0..=4) { + 0 => entity.with_asset_expect("common.entity.dungeon.tier-2.bow"), + 1 => entity.with_asset_expect("common.entity.dungeon.tier-2.staff"), + _ => entity.with_asset_expect("common.entity.dungeon.tier-2.spear"), + } + }); + + entities } -fn enemy_3(dynamic_rng: &mut impl Rng, entity: EntityInfo) -> EntityInfo { - match dynamic_rng.gen_range(0..5) { - 0 => entity - .with_body(comp::Body::Object(comp::object::Body::HaniwaSentry)) - .with_asset_expect("common.entity.dungeon.tier-3.sentry"), - 1 => entity.with_asset_expect("common.entity.dungeon.tier-3.bow"), - 2 => entity.with_asset_expect("common.entity.dungeon.tier-3.staff"), - _ => entity.with_asset_expect("common.entity.dungeon.tier-3.spear"), - } -} -fn enemy_4(dynamic_rng: &mut impl Rng, entity: EntityInfo) -> EntityInfo { - match dynamic_rng.gen_range(0..5) { - 0 => entity.with_asset_expect("common.entity.dungeon.tier-4.bow"), - 1 => entity.with_asset_expect("common.entity.dungeon.tier-4.staff"), - _ => entity.with_asset_expect("common.entity.dungeon.tier-4.spear"), - } +fn enemy_3(dynamic_rng: &mut impl Rng, tile_wcenter: Vec3) -> Vec { + let number = dynamic_rng.gen_range(2..=4); + let mut entities = Vec::new(); + entities.resize_with(number, || { + let entity = EntityInfo::at(tile_wcenter.map(|e| e as f32)); + match dynamic_rng.gen_range(0..=4) { + 0 => entity.with_asset_expect("common.entity.dungeon.tier-3.bow"), + 1 => entity.with_asset_expect("common.entity.dungeon.tier-3.staff"), + _ => entity.with_asset_expect("common.entity.dungeon.tier-3.spear"), + } + }); + + entities } -fn enemy_5(dynamic_rng: &mut impl Rng, entity: EntityInfo) -> EntityInfo { - match dynamic_rng.gen_range(0..6) { - 0 => entity - .with_body(comp::Body::Object(comp::object::Body::Crossbow)) - .with_asset_expect("common.entity.dungeon.tier-5.turret"), - 1 => entity.with_asset_expect("common.entity.dungeon.tier-5.warlock"), - 2 => entity.with_asset_expect("common.entity.dungeon.tier-5.warlord"), - _ => entity.with_asset_expect("common.entity.dungeon.tier-5.cultist"), - } +fn enemy_4(dynamic_rng: &mut impl Rng, tile_wcenter: Vec3) -> Vec { + let number = dynamic_rng.gen_range(2..=4); + let mut entities = Vec::new(); + entities.resize_with(number, || { + let entity = EntityInfo::at(tile_wcenter.map(|e| e as f32)); + match dynamic_rng.gen_range(0..=4) { + 0 => entity.with_asset_expect("common.entity.dungeon.tier-4.bow"), + 1 => entity.with_asset_expect("common.entity.dungeon.tier-4.staff"), + _ => entity.with_asset_expect("common.entity.dungeon.tier-4.spear"), + } + }); + + entities } -fn enemy_fallback(entity: EntityInfo) -> EntityInfo { - entity.with_asset_expect("common.entity.dungeon.fallback.enemy") +fn enemy_5(dynamic_rng: &mut impl Rng, tile_wcenter: Vec3) -> Vec { + let number = dynamic_rng.gen_range(1..=3); + let mut entities = Vec::new(); + entities.resize_with(number, || { + let entity = EntityInfo::at(tile_wcenter.map(|e| e as f32)); + match dynamic_rng.gen_range(0..=4) { + 0 => entity.with_asset_expect("common.entity.dungeon.tier-5.warlock"), + 1 => entity.with_asset_expect("common.entity.dungeon.tier-5.warlord"), + _ => entity.with_asset_expect("common.entity.dungeon.tier-5.cultist"), + } + }); + + entities +} + +fn enemy_fallback(dynamic_rng: &mut impl Rng, tile_wcenter: Vec3) -> Vec { + let number = dynamic_rng.gen_range(2..=4); + let mut entities = Vec::new(); + entities.resize_with(number, || { + let entity = EntityInfo::at(tile_wcenter.map(|e| e as f32)); + entity.with_asset_expect("common.entity.dungeon.fallback.enemy") + }); + + entities } fn boss_0(tile_wcenter: Vec3) -> Vec { @@ -782,7 +856,7 @@ fn mini_boss_4(tile_wcenter: Vec3) -> Vec { fn mini_boss_5(dynamic_rng: &mut impl Rng, tile_wcenter: Vec3) -> Vec { let mut entities = Vec::new(); - match dynamic_rng.gen_range(0..3) { + match dynamic_rng.gen_range(0..=2) { 0 => { entities.push( EntityInfo::at(tile_wcenter.map(|e| e as f32)) @@ -816,51 +890,6 @@ fn mini_boss_fallback(tile_wcenter: Vec3) -> Vec { ] } -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_creating_bosses() { - let tile_wcenter = Vec3::new(0, 0, 0); - boss_0(tile_wcenter); - boss_1(tile_wcenter); - boss_2(tile_wcenter); - boss_3(tile_wcenter); - boss_4(tile_wcenter); - boss_5(tile_wcenter); - boss_fallback(tile_wcenter); - } - - #[test] - // FIXME: Uses random, test may be not great - fn test_creating_enemies() { - let mut dynamic_rng = rand::thread_rng(); - let raw_entity = EntityInfo::at(Vec3::new(0.0, 0.0, 0.0)); - enemy_0(&mut dynamic_rng, raw_entity.clone()); - enemy_1(&mut dynamic_rng, raw_entity.clone()); - enemy_2(&mut dynamic_rng, raw_entity.clone()); - enemy_3(&mut dynamic_rng, raw_entity.clone()); - enemy_4(&mut dynamic_rng, raw_entity.clone()); - enemy_5(&mut dynamic_rng, raw_entity.clone()); - enemy_fallback(raw_entity); - } - - #[test] - // FIXME: Uses random, test may be not great - fn test_creating_minibosses() { - let mut dynamic_rng = rand::thread_rng(); - let tile_wcenter = Vec3::new(0, 0, 0); - mini_boss_0(tile_wcenter); - mini_boss_1(tile_wcenter); - mini_boss_2(tile_wcenter); - mini_boss_3(tile_wcenter); - mini_boss_4(tile_wcenter); - mini_boss_5(&mut dynamic_rng, tile_wcenter); - mini_boss_fallback(tile_wcenter); - } -} - pub fn tilegrid_nearest_wall(tiles: &Grid, rpos: Vec2) -> Option> { let tile_pos = rpos.map(|e| e.div_euclid(TILE_SIZE)); @@ -1334,3 +1363,48 @@ impl SiteStructure for Dungeon { } } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_creating_bosses() { + let tile_wcenter = Vec3::new(0, 0, 0); + boss_0(tile_wcenter); + boss_1(tile_wcenter); + boss_2(tile_wcenter); + boss_3(tile_wcenter); + boss_4(tile_wcenter); + boss_5(tile_wcenter); + boss_fallback(tile_wcenter); + } + + #[test] + // FIXME: Uses random, test may be not great + fn test_creating_enemies() { + let mut dynamic_rng = rand::thread_rng(); + let random_position = Vec3::new(0, 0, 0); + enemy_0(&mut dynamic_rng, random_position); + enemy_1(&mut dynamic_rng, random_position); + enemy_2(&mut dynamic_rng, random_position); + enemy_3(&mut dynamic_rng, random_position); + enemy_4(&mut dynamic_rng, random_position); + enemy_5(&mut dynamic_rng, random_position); + enemy_fallback(&mut dynamic_rng, random_position); + } + + #[test] + // FIXME: Uses random, test may be not great + fn test_creating_minibosses() { + let mut dynamic_rng = rand::thread_rng(); + let tile_wcenter = Vec3::new(0, 0, 0); + mini_boss_0(tile_wcenter); + mini_boss_1(tile_wcenter); + mini_boss_2(tile_wcenter); + mini_boss_3(tile_wcenter); + mini_boss_4(tile_wcenter); + mini_boss_5(&mut dynamic_rng, tile_wcenter); + mini_boss_fallback(tile_wcenter); + } +} From 032918e22b60c820fa31f4561a26cc926c048d1c Mon Sep 17 00:00:00 2001 From: juliancoffee Date: Sun, 27 Jun 2021 23:10:31 +0300 Subject: [PATCH 07/12] Nerf bow repeater knockback --- assets/common/abilities/bow/repeater.ron | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/common/abilities/bow/repeater.ron b/assets/common/abilities/bow/repeater.ron index 4ba4737c82..bbffcbb587 100644 --- a/assets/common/abilities/bow/repeater.ron +++ b/assets/common/abilities/bow/repeater.ron @@ -7,7 +7,7 @@ RepeaterRanged( half_speed_at: 1, projectile: Arrow( damage: 30.0, - knockback: 5.0, + knockback: 2.0, energy_regen: 0, ), projectile_body: Object(Arrow), From ab1c93c21eb86f21f44870db150ce4dcd90b86fe Mon Sep 17 00:00:00 2001 From: juliancoffee Date: Sun, 27 Jun 2021 23:11:04 +0300 Subject: [PATCH 08/12] Buff Mindflayer flamethrower - Buff DPS twice: increase tick_rate from 0.9 to 5.0 (x5.5), while reducing damage from 400 to 150 (x2.6) - Make ori_rate slower: 0.2 from 0.6 and make it use FromOri orientation behaviour --- .../common/abilities/custom/mindflayer/cursedflames.ron | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/assets/common/abilities/custom/mindflayer/cursedflames.ron b/assets/common/abilities/custom/mindflayer/cursedflames.ron index 0cfddbd5f5..b74005d138 100644 --- a/assets/common/abilities/custom/mindflayer/cursedflames.ron +++ b/assets/common/abilities/custom/mindflayer/cursedflames.ron @@ -2,8 +2,8 @@ BasicBeam( buildup_duration: 0.40, recover_duration: 0.50, beam_duration: 1.0, - damage: 400, - tick_rate: 0.9, + damage: 150, + tick_rate: 5.0, range: 22.0, max_angle: 15.0, damage_effect: Some(Buff(( @@ -14,7 +14,7 @@ BasicBeam( ))), energy_regen: 0, energy_drain: 0, - orientation_behavior: Normal, - ori_rate: 0.6, + orientation_behavior: FromOri, + ori_rate: 0.2, specifier: Cultist, ) From 8a8a3f93184d0bd03099623f4587864aa6426227 Mon Sep 17 00:00:00 2001 From: juliancoffee Date: Mon, 28 Jun 2021 13:12:35 +0300 Subject: [PATCH 09/12] Add RoomKind enum, address comments - enum RoomKind {Peaceful, Fight, Boss, Miniboss} to specify type of generated room. --- world/src/site2/plot/dungeon.rs | 44 +++++++++++++++------------------ 1 file changed, 20 insertions(+), 24 deletions(-) diff --git a/world/src/site2/plot/dungeon.rs b/world/src/site2/plot/dungeon.rs index a4780c7634..d912aaee50 100644 --- a/world/src/site2/plot/dungeon.rs +++ b/world/src/site2/plot/dungeon.rs @@ -184,12 +184,18 @@ impl Tile { } } +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +enum RoomKind { + Peaceful, + Fight, + Boss, + Miniboss, +} + pub struct Room { seed: u32, loot_density: f32, - fight: bool, - miniboss: bool, - boss: bool, + kind: RoomKind, area: Rect, height: i32, pillars: Option, // Pillars with the given separation @@ -252,9 +258,7 @@ impl Floor { let upstair_room = this.create_room(Room { seed: ctx.rng.gen(), loot_density: 0.0, - fight: false, - miniboss: false, - boss: false, + kind: RoomKind::Peaceful, area: Rect::from((stair_tile - tile_offset - 1, Extent2::broadcast(3))), height: STAIR_ROOM_HEIGHT, pillars: None, @@ -265,10 +269,7 @@ impl Floor { this.create_room(Room { seed: ctx.rng.gen(), loot_density: 0.0, - // Our bosses are intelligent enough to summon minions if needed. - fight: false, - miniboss: false, - boss: true, + kind: RoomKind::Boss, area: Rect::from(( new_stair_tile - tile_offset - MAX_WIDTH as i32 - 1, Extent2::broadcast(width as i32 * 2 + 1), @@ -282,9 +283,7 @@ impl Floor { let downstair_room = this.create_room(Room { seed: ctx.rng.gen(), loot_density: 0.0, - fight: false, - miniboss: false, - boss: false, + kind: RoomKind::Peaceful, area: Rect::from((new_stair_tile - tile_offset - 1, Extent2::broadcast(3))), height: STAIR_ROOM_HEIGHT, pillars: None, @@ -363,9 +362,7 @@ impl Floor { 0 => self.create_room(Room { seed: ctx.rng.gen(), loot_density: 0.000025 + level as f32 * 0.00015, - fight: false, - miniboss: true, - boss: false, + kind: RoomKind::Miniboss, area, height: ctx.rng.gen_range(15..20), pillars: Some(ctx.rng.gen_range(2..=4)), @@ -375,9 +372,7 @@ impl Floor { _ => self.create_room(Room { seed: ctx.rng.gen(), loot_density: 0.000025 + level as f32 * 0.00015, - fight: true, - miniboss: false, - boss: false, + kind: RoomKind::Fight, area, height: ctx.rng.gen_range(10..15), pillars: if ctx.rng.gen_range(0..4) == 0 { @@ -480,7 +475,7 @@ impl Floor { ); // Fill regular rooms - if room.fight { + if room.kind == RoomKind::Fight { let enemy_spawn_tile = room.area.center(); // Don't spawn enemies in a pillar let enemy_tile_is_pillar = room.pillars.map_or(false, |pillar_space| { @@ -520,6 +515,7 @@ impl Floor { } } else { // Turrets + // Turret has 1/5000 chance to spawn per voxel in fight room if dynamic_rng.gen_range(0..5000) == 0 { let pos = tile_wcenter.map(|e| e as f32) + Vec3::::iota() @@ -561,7 +557,7 @@ impl Floor { } // Spawn miniboss (or minibosses) - if room.miniboss { + if room.kind == RoomKind::Miniboss { let miniboss_spawn_tile = room.area.center(); // Don't spawn the miniboss in a pillar let miniboss_tile_is_pillar = room.pillars.map_or(false, |pillar_space| { @@ -603,7 +599,7 @@ impl Floor { } // Spawn miniboss - if room.boss { + if room.kind == RoomKind::Miniboss { let boss_spawn_tile = room.area.center(); // Don't spawn the boss in a pillar let boss_tile_is_pillar = room.pillars.map_or(false, |pillar_space| { @@ -1251,7 +1247,7 @@ impl Floor { prim(Primitive::Scale(pillar, Vec2::broadcast(scale).with_z(1.0))); lights = prim(Primitive::And(lighting_plane, lights)); // Only add the base (and shift the lights up) for boss-room pillars - if room.boss { + if room.kind == RoomKind::Boss { lights = prim(Primitive::Translate(lights, 3 * Vec3::unit_z())); pillar = prim(Primitive::Or(pillar, base)); } @@ -1260,7 +1256,7 @@ impl Floor { } // Keep track of the boss room to be able to add decorations later - if room.boss { + if room.kind == RoomKind::Boss { boss_room_center = Some(floor_corner + TILE_SIZE * room.area.center() + TILE_SIZE / 2); } From def73d442581e52304cf31b005f2fcd1e2437c9e Mon Sep 17 00:00:00 2001 From: juliancoffee Date: Tue, 29 Jun 2021 19:06:40 +0300 Subject: [PATCH 10/12] Split room enemy generation to separate functions --- world/src/site2/plot/dungeon.rs | 358 +++++++++++++++++--------------- 1 file changed, 194 insertions(+), 164 deletions(-) diff --git a/world/src/site2/plot/dungeon.rs b/world/src/site2/plot/dungeon.rs index d912aaee50..5f4cf61a1a 100644 --- a/world/src/site2/plot/dungeon.rs +++ b/world/src/site2/plot/dungeon.rs @@ -202,6 +202,177 @@ pub struct Room { difficulty: u32, } +impl Room { + fn fill_fight_cell( + &self, + supplement: &mut ChunkSupplement, + dynamic_rng: &mut impl Rng, + tile_wcenter: Vec3, + wpos2d: Vec2, + tile_pos: Vec2, + ) { + let enemy_spawn_tile = self.area.center(); + // Don't spawn enemies in a pillar + let enemy_tile_is_pillar = self.pillars.map_or(false, |pillar_space| { + enemy_spawn_tile + .map(|e| e.rem_euclid(pillar_space) == 0) + .reduce_and() + }); + let enemy_spawn_tile = enemy_spawn_tile + if enemy_tile_is_pillar { 1 } else { 0 }; + + // Toss mobs in the center of the room + if tile_pos == enemy_spawn_tile && wpos2d == tile_wcenter.xy() { + let entities = match self.difficulty { + 0 => enemy_0(dynamic_rng, tile_wcenter), + 1 => enemy_1(dynamic_rng, tile_wcenter), + 2 => enemy_2(dynamic_rng, tile_wcenter), + 3 => enemy_3(dynamic_rng, tile_wcenter), + 4 => enemy_4(dynamic_rng, tile_wcenter), + 5 => enemy_5(dynamic_rng, tile_wcenter), + _ => enemy_fallback(dynamic_rng, tile_wcenter), + }; + + for entity in entities { + supplement.add_entity( + entity + .with_level( + dynamic_rng + .gen_range( + (self.difficulty as f32).powf(1.25) + 3.0 + ..(self.difficulty as f32).powf(1.5) + 4.0, + ) + .round() as u16, + ) + .with_alignment(comp::Alignment::Enemy), + ); + } + } else { + // Turrets + // Turret has 1/5000 chance to spawn per voxel in fight room + if dynamic_rng.gen_range(0..5000) == 0 { + let pos = tile_wcenter.map(|e| e as f32) + + Vec3::::iota() + .map(|e| { + (RandomField::new(self.seed.wrapping_add(10 + e)) + .get(Vec3::from(tile_pos)) + % 32) as i32 + - 16 + }) + .map(|e| e as f32 / 16.0); + let turret = + EntityInfo::at(pos.map(|e| e as f32)).with_alignment(comp::Alignment::Enemy); + match self.difficulty { + 3 => { + let turret = turret + .with_body(comp::Body::Object(comp::object::Body::Crossbow)) + .with_asset_expect("common.entity.dungeon.tier-3.sentry"); + supplement.add_entity(turret); + }, + 5 => { + let turret = turret + .with_body(comp::Body::Object(comp::object::Body::Crossbow)) + .with_asset_expect("common.entity.dungeon.tier-5.turret"); + supplement.add_entity(turret); + }, + _ => {}, + }; + } + } + } + + fn fill_miniboss_cell( + &self, + supplement: &mut ChunkSupplement, + dynamic_rng: &mut impl Rng, + tile_wcenter: Vec3, + wpos2d: Vec2, + tile_pos: Vec2, + ) { + let miniboss_spawn_tile = self.area.center(); + // Don't spawn the miniboss in a pillar + let miniboss_tile_is_pillar = self.pillars.map_or(false, |pillar_space| { + miniboss_spawn_tile + .map(|e| e.rem_euclid(pillar_space) == 0) + .reduce_and() + }); + let miniboss_spawn_tile = miniboss_spawn_tile + if miniboss_tile_is_pillar { 1 } else { 0 }; + + if tile_pos == miniboss_spawn_tile && tile_wcenter.xy() == wpos2d { + let entities = match self.difficulty { + 0 => mini_boss_0(tile_wcenter), + 1 => mini_boss_1(tile_wcenter), + 2 => mini_boss_2(tile_wcenter), + 3 => mini_boss_3(tile_wcenter), + 4 => mini_boss_4(tile_wcenter), + 5 => mini_boss_5(dynamic_rng, tile_wcenter), + _ => mini_boss_fallback(tile_wcenter), + }; + + for entity in entities { + supplement.add_entity( + entity + .with_level( + dynamic_rng + .gen_range( + (self.difficulty as f32).powf(1.25) + 3.0 + ..(self.difficulty as f32).powf(1.5) + 4.0, + ) + .round() as u16 + * 5, + ) + .with_alignment(comp::Alignment::Enemy), + ); + } + } + } + + fn fill_boss_cell( + &self, + supplement: &mut ChunkSupplement, + dynamic_rng: &mut impl Rng, + tile_wcenter: Vec3, + wpos2d: Vec2, + tile_pos: Vec2, + ) { + let boss_spawn_tile = self.area.center(); + // Don't spawn the boss in a pillar + let boss_tile_is_pillar = self.pillars.map_or(false, |pillar_space| { + boss_spawn_tile + .map(|e| e.rem_euclid(pillar_space) == 0) + .reduce_and() + }); + let boss_spawn_tile = boss_spawn_tile + if boss_tile_is_pillar { 1 } else { 0 }; + + if tile_pos == boss_spawn_tile && wpos2d == tile_wcenter.xy() { + let entities = match self.difficulty { + 0 => boss_0(tile_wcenter), + 1 => boss_1(tile_wcenter), + 2 => boss_2(tile_wcenter), + 3 => boss_3(tile_wcenter), + 4 => boss_4(tile_wcenter), + 5 => boss_5(tile_wcenter), + _ => boss_fallback(tile_wcenter), + }; + + for entity in entities { + supplement.add_entity( + entity + .with_level( + dynamic_rng + .gen_range( + (self.difficulty as f32).powf(1.25) + 3.0 + ..(self.difficulty as f32).powf(1.5) + 4.0, + ) + .round() as u16 + * 5, + ) + .with_alignment(comp::Alignment::Enemy), + ); + } + } + } +} + struct Floor { tile_offset: Vec2, tiles: Grid, @@ -474,170 +645,29 @@ impl Floor { .map(|e| e.div_euclid(TILE_SIZE) * TILE_SIZE + TILE_SIZE / 2), ); - // Fill regular rooms - if room.kind == RoomKind::Fight { - let enemy_spawn_tile = room.area.center(); - // Don't spawn enemies in a pillar - let enemy_tile_is_pillar = room.pillars.map_or(false, |pillar_space| { - enemy_spawn_tile - .map(|e| e.rem_euclid(pillar_space) == 0) - .reduce_and() - }); - let enemy_spawn_tile = - enemy_spawn_tile + if enemy_tile_is_pillar { 1 } else { 0 }; - - // Toss mobs in the center of the room - if tile_pos == enemy_spawn_tile && wpos2d == tile_wcenter.xy() { - let entities = match room.difficulty { - 0 => enemy_0(dynamic_rng, tile_wcenter), - 1 => enemy_1(dynamic_rng, tile_wcenter), - 2 => enemy_2(dynamic_rng, tile_wcenter), - 3 => enemy_3(dynamic_rng, tile_wcenter), - 4 => enemy_4(dynamic_rng, tile_wcenter), - 5 => enemy_5(dynamic_rng, tile_wcenter), - _ => enemy_fallback(dynamic_rng, tile_wcenter), - }; - - for entity in entities { - supplement.add_entity( - entity - .with_level( - dynamic_rng - .gen_range( - (room.difficulty as f32).powf(1.25) + 3.0 - ..(room.difficulty as f32).powf(1.5) + 4.0, - ) - .round() - as u16, - ) - .with_alignment(comp::Alignment::Enemy), - ); - } - } else { - // Turrets - // Turret has 1/5000 chance to spawn per voxel in fight room - if dynamic_rng.gen_range(0..5000) == 0 { - let pos = tile_wcenter.map(|e| e as f32) - + Vec3::::iota() - .map(|e| { - (RandomField::new(room.seed.wrapping_add(10 + e)) - .get(Vec3::from(tile_pos)) - % 32) - as i32 - - 16 - }) - .map(|e| e as f32 / 16.0); - let turret = EntityInfo::at(pos.map(|e| e as f32)) - .with_alignment(comp::Alignment::Enemy); - match room.difficulty { - 3 => { - let turret = turret - .with_body(comp::Body::Object( - comp::object::Body::Crossbow, - )) - .with_asset_expect( - "common.entity.dungeon.tier-3.sentry", - ); - supplement.add_entity(turret); - }, - 5 => { - let turret = turret - .with_body(comp::Body::Object( - comp::object::Body::Crossbow, - )) - .with_asset_expect( - "common.entity.dungeon.tier-5.turret", - ); - supplement.add_entity(turret); - }, - _ => {}, - }; - } - } - } - - // Spawn miniboss (or minibosses) - if room.kind == RoomKind::Miniboss { - let miniboss_spawn_tile = room.area.center(); - // Don't spawn the miniboss in a pillar - let miniboss_tile_is_pillar = room.pillars.map_or(false, |pillar_space| { - miniboss_spawn_tile - .map(|e| e.rem_euclid(pillar_space) == 0) - .reduce_and() - }); - let miniboss_spawn_tile = - miniboss_spawn_tile + if miniboss_tile_is_pillar { 1 } else { 0 }; - - if tile_pos == miniboss_spawn_tile && tile_wcenter.xy() == wpos2d { - let entities = match room.difficulty { - 0 => mini_boss_0(tile_wcenter), - 1 => mini_boss_1(tile_wcenter), - 2 => mini_boss_2(tile_wcenter), - 3 => mini_boss_3(tile_wcenter), - 4 => mini_boss_4(tile_wcenter), - 5 => mini_boss_5(dynamic_rng, tile_wcenter), - _ => mini_boss_fallback(tile_wcenter), - }; - - for entity in entities { - supplement.add_entity( - entity - .with_level( - dynamic_rng - .gen_range( - (room.difficulty as f32).powf(1.25) + 3.0 - ..(room.difficulty as f32).powf(1.5) + 4.0, - ) - .round() - as u16 - * 5, - ) - .with_alignment(comp::Alignment::Enemy), - ); - } - } - } - - // Spawn miniboss - if room.kind == RoomKind::Miniboss { - let boss_spawn_tile = room.area.center(); - // Don't spawn the boss in a pillar - let boss_tile_is_pillar = room.pillars.map_or(false, |pillar_space| { - boss_spawn_tile - .map(|e| e.rem_euclid(pillar_space) == 0) - .reduce_and() - }); - let boss_spawn_tile = - boss_spawn_tile + if boss_tile_is_pillar { 1 } else { 0 }; - - if tile_pos == boss_spawn_tile && wpos2d == tile_wcenter.xy() { - let entities = match room.difficulty { - 0 => boss_0(tile_wcenter), - 1 => boss_1(tile_wcenter), - 2 => boss_2(tile_wcenter), - 3 => boss_3(tile_wcenter), - 4 => boss_4(tile_wcenter), - 5 => boss_5(tile_wcenter), - _ => boss_fallback(tile_wcenter), - }; - - for entity in entities { - supplement.add_entity( - entity - .with_level( - dynamic_rng - .gen_range( - (room.difficulty as f32).powf(1.25) + 3.0 - ..(room.difficulty as f32).powf(1.5) + 4.0, - ) - .round() - as u16 - * 5, - ) - .with_alignment(comp::Alignment::Enemy), - ); - } - } + match room.kind { + RoomKind::Fight => room.fill_fight_cell( + supplement, + dynamic_rng, + tile_wcenter, + wpos2d, + tile_pos, + ), + RoomKind::Miniboss => room.fill_miniboss_cell( + supplement, + dynamic_rng, + tile_wcenter, + wpos2d, + tile_pos, + ), + RoomKind::Boss => room.fill_boss_cell( + supplement, + dynamic_rng, + tile_wcenter, + wpos2d, + tile_pos, + ), + RoomKind::Peaceful => {}, } } } From 0dcf76333a53cec7757dcb5fc5ad899df6108534 Mon Sep 17 00:00:00 2001 From: juliancoffee Date: Wed, 30 Jun 2021 01:22:18 +0300 Subject: [PATCH 11/12] Nerf hammer poise --- assets/common/abilities/hammer/charged.ron | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/assets/common/abilities/hammer/charged.ron b/assets/common/abilities/hammer/charged.ron index c148f9695e..9890a9a464 100644 --- a/assets/common/abilities/hammer/charged.ron +++ b/assets/common/abilities/hammer/charged.ron @@ -3,10 +3,10 @@ ChargedMelee( energy_drain: 300, initial_damage: 10, scaled_damage: 160, - initial_poise_damage: 60, - scaled_poise_damage: 130, - initial_knockback: 10.0, - scaled_knockback: 50.0, + initial_poise_damage: 20, + scaled_poise_damage: 60, + initial_knockback: 5.0, + scaled_knockback: 20.0, range: 3.5, max_angle: 30.0, speed: 1.0, From 63fddc2516b56b668c030ce25e18560b39c93fd4 Mon Sep 17 00:00:00 2001 From: juliancoffee Date: Tue, 29 Jun 2021 21:25:43 +0300 Subject: [PATCH 12/12] Introduce maxed skillsets for melee and staff --- assets/common/skillset/preset/max/axe.ron | 23 +++++++++++++++++ assets/common/skillset/preset/max/bow.ron | 25 +++++++++++++++++++ assets/common/skillset/preset/max/hammer.ron | 23 +++++++++++++++++ assets/common/skillset/preset/max/staff.ron | 21 ++++++++++++++++ assets/common/skillset/preset/max/sword.ron | 26 ++++++++++++++++++++ 5 files changed, 118 insertions(+) create mode 100644 assets/common/skillset/preset/max/axe.ron create mode 100644 assets/common/skillset/preset/max/bow.ron create mode 100644 assets/common/skillset/preset/max/hammer.ron create mode 100644 assets/common/skillset/preset/max/staff.ron create mode 100644 assets/common/skillset/preset/max/sword.ron diff --git a/assets/common/skillset/preset/max/axe.ron b/assets/common/skillset/preset/max/axe.ron new file mode 100644 index 0000000000..1ad1261f42 --- /dev/null +++ b/assets/common/skillset/preset/max/axe.ron @@ -0,0 +1,23 @@ +([ + Group(Weapon(Axe)), + + // DoubleStrike + Skill((Axe(DsCombo), None)), + Skill((Axe(DsDamage), Some(3))), + Skill((Axe(DsRegen), Some(2))), + Skill((Axe(DsSpeed), Some(3))), + + // Spin + Skill((Axe(SInfinite), None)), + Skill((Axe(SHelicopter), None)), + Skill((Axe(SDamage), Some(3))), + Skill((Axe(SSpeed), Some(2))), + Skill((Axe(SCost), Some(2))), + + // Leap + Skill((Axe(UnlockLeap), None)), + Skill((Axe(LDamage), Some(2))), + Skill((Axe(LKnockback), Some(2))), + Skill((Axe(LCost), Some(2))), + Skill((Axe(LDistance), Some(2))), +]) diff --git a/assets/common/skillset/preset/max/bow.ron b/assets/common/skillset/preset/max/bow.ron new file mode 100644 index 0000000000..cef119d0ec --- /dev/null +++ b/assets/common/skillset/preset/max/bow.ron @@ -0,0 +1,25 @@ +([ + Group(Weapon(Bow)), + + // Projectile Speed + Skill((Bow(ProjSpeed), Some(2))), + + // Charged + Skill((Bow(CDamage), Some(3))), + Skill((Bow(CKnockback), Some(2))), + Skill((Bow(CSpeed), Some(2))), + Skill((Bow(CRegen), Some(2))), + Skill((Bow(CMove), Some(2))), + + // Repeater + Skill((Bow(RDamage), Some(3))), + Skill((Bow(RCost), Some(2))), + Skill((Bow(RSpeed), Some(2))), + + // Shotgun + Skill((Bow(UnlockShotgun), None)), + Skill((Bow(SDamage), Some(2))), + Skill((Bow(SCost), Some(2))), + Skill((Bow(SArrows), Some(2))), + Skill((Bow(SSpread), Some(2))), +]) diff --git a/assets/common/skillset/preset/max/hammer.ron b/assets/common/skillset/preset/max/hammer.ron new file mode 100644 index 0000000000..6de9d2bb50 --- /dev/null +++ b/assets/common/skillset/preset/max/hammer.ron @@ -0,0 +1,23 @@ +([ + Group(Weapon(Hammer)), + + // Single Strike, as single as you are + Skill((Hammer(SsKnockback), Some(2))), + Skill((Hammer(SsDamage), Some(3))), + Skill((Hammer(SsRegen), Some(2))), + Skill((Hammer(SsSpeed), Some(3))), + + // Charged + Skill((Hammer(CDamage), Some(3))), + Skill((Hammer(CKnockback), Some(3))), + Skill((Hammer(CDrain), Some(2))), + Skill((Hammer(CSpeed), Some(2))), + + // Leap + Skill((Hammer(UnlockLeap), None)), + Skill((Hammer(LDamage), Some(2))), + Skill((Hammer(LCost), Some(2))), + Skill((Hammer(LDistance), Some(2))), + Skill((Hammer(LKnockback), Some(2))), + Skill((Hammer(LRange), Some(2))), +]) diff --git a/assets/common/skillset/preset/max/staff.ron b/assets/common/skillset/preset/max/staff.ron new file mode 100644 index 0000000000..d2511166dc --- /dev/null +++ b/assets/common/skillset/preset/max/staff.ron @@ -0,0 +1,21 @@ +([ + Group(Weapon(Staff)), + + // Fireball + Skill((Staff(BDamage), Some(3))), + Skill((Staff(BRegen), Some(2))), + Skill((Staff(BRadius), Some(3))), + + // Flamethrower + Skill((Staff(FRange), Some(2))), + Skill((Staff(FDamage), Some(3))), + Skill((Staff(FDrain), Some(2))), + Skill((Staff(FVelocity), Some(2))), + + // Shockwave + Skill((Staff(UnlockShockwave), None)), + Skill((Staff(SDamage), Some(2))), + Skill((Staff(SKnockback), Some(2))), + Skill((Staff(SRange), Some(2))), + Skill((Staff(SCost), Some(2))), +]) diff --git a/assets/common/skillset/preset/max/sword.ron b/assets/common/skillset/preset/max/sword.ron new file mode 100644 index 0000000000..8781f8ebae --- /dev/null +++ b/assets/common/skillset/preset/max/sword.ron @@ -0,0 +1,26 @@ +([ + Group(Weapon(Sword)), + + Skill((Sword(InterruptingAttacks), None)), + + // TripleStrike + Skill((Sword(TsCombo), None)), + Skill((Sword(TsDamage), Some(3))), + Skill((Sword(TsRegen), Some(2))), + Skill((Sword(TsSpeed), Some(3))), + + // Dash + Skill((Sword(DCost), Some(2))), + Skill((Sword(DDrain), Some(2))), + Skill((Sword(DDamage), Some(2))), + Skill((Sword(DScaling), Some(3))), + Skill((Sword(DSpeed), None)), + Skill((Sword(DInfinite), None)), + + // Spin of death + Skill((Sword(UnlockSpin), None)), + Skill((Sword(SDamage), Some(2))), + Skill((Sword(SSpeed), Some(2))), + Skill((Sword(SSpins), Some(2))), + Skill((Sword(SCost), Some(2))), +])