Merge branch 'halloween' into 'master'

Halloween event

See merge request veloren/veloren!3668
This commit is contained in:
Marcel 2022-10-25 21:37:34 +00:00
commit 51d4c8dd51
55 changed files with 759 additions and 17 deletions

View File

@ -121,6 +121,11 @@
(None, "common.abilities.custom.woodgolem.shockwave")
],
),
Custom("Simple Flying Melee"): (
primary: "common.abilities.custom.simpleflyingmelee.singlestrike",
secondary: "common.abilities.custom.simpleflyingmelee.singlestrike",
abilities: [],
),
Custom("Sword Simple"): (
primary: "common.abilities.swordsimple.doublestrike",
secondary: "common.abilities.swordsimple.dash",

View File

@ -0,0 +1,28 @@
ComboMelee(
stage_data: [
(
stage: 1,
base_damage: 1.0,
damage_increase: 0,
base_poise_damage: 0,
poise_damage_increase: 0,
knockback: 0.0,
range: 2.5,
angle: 150.0,
base_buildup_duration: 0.1,
base_swing_duration: 0.07,
hit_timing: 0.5,
base_recover_duration: 0.2,
forward_movement: 0.0,
damage_kind: Piercing,
),
],
initial_energy_gain: 0,
max_energy_gain: 0,
energy_increase: 0,
speed_increase: 0.0,
max_speed_increase: 0.0,
scales_from_combo: 0,
is_interruptible: false,
ori_modifier: 0.6,
)

View File

@ -0,0 +1,11 @@
#![enable(implicit_some)]
(
name: Name("Dullahan"),
body: RandomWith("dullahan"),
alignment: Alignment(Enemy),
loot: LootTable("common.loot_tables.calendar.halloween.halloween_dullahan"),
inventory: (
loadout: FromBody,
),
meta: [],
)

View File

@ -0,0 +1,11 @@
#![enable(implicit_some)]
(
name: Name("Harvester"),
body: RandomWith("harvester"),
alignment: Alignment(Enemy),
loot: LootTable("common.loot_tables.calendar.halloween.halloween_harvester"),
inventory: (
loadout: FromBody,
),
meta: [],
)

View File

@ -0,0 +1,24 @@
#![enable(implicit_some)]
(
name: Name("Trickster"),
body: RandomWith("draugr"),
alignment: Alignment(Enemy),
loot: LootTable("common.loot_tables.calendar.halloween.trickster"),
inventory: (
loadout: Inline((
inherit: Asset("common.loadout.calendar.halloween.trickster"),
active_hands: InHands((Choice([
(1, Item("common.items.tool.instruments.bass")),
(1, Item("common.items.tool.instruments.flute")),
(1, Item("common.items.tool.instruments.harp")),
(1, Item("common.items.tool.instruments.kalimba")),
(1, Item("common.items.tool.instruments.sitar")),
(1, Item("common.items.tool.instruments.perc")),
(1, Item("common.items.tool.instruments.lute")),
(1, Item("common.items.tool.instruments.guitar")),
(1, Item("common.items.tool.instruments.melodica")),
]), None)),
)),
),
meta: [],
)

View File

@ -0,0 +1,11 @@
#![enable(implicit_some)]
(
name: Automatic,
body: RandomWith("bat"),
alignment: Alignment(Enemy),
loot: LootTable("common.loot_tables.creature.bat"),
inventory: (
loadout: FromBody,
),
meta: [],
)

View File

@ -0,0 +1,13 @@
ItemDef(
name: "Pumpkin Head.",
description: "Halloween attire.",
kind: Armor((
kind: Head,
stats: Direct((
energy_max: Some(4.0),
energy_reward: Some(0.04),
)),
)),
quality: Common,
tags: [],
)

View File

@ -0,0 +1,22 @@
ItemDef(
name: "Honeycorn",
description: "Sweeet",
kind: Consumable(
kind: Food,
effects: [
Buff((
kind: Saturation,
data: (
strength: 4.0,
duration: Some((
secs: 5,
nanos: 0,
)),
),
cat_ids: [Natural],
)),
]
),
quality: Common,
tags: [Food],
)

View File

@ -0,0 +1,22 @@
ItemDef(
name: "Pumpkin Spice Brew",
description: "Brewed from moldy pumpkins.",
kind: Consumable(
kind: Drink,
effects: [
Buff((
kind: Saturation,
data: (
strength: 10.0,
duration: Some((
secs: 5,
nanos: 0,
)),
),
cat_ids: [Natural],
)),
]
),
quality: Moderate,
tags: [Food],
)

View File

@ -0,0 +1,21 @@
ItemDef(
name: "Simple Flying Melee",
description: "I believe I can fly!!!!!",
kind: Tool((
kind: Natural,
hands: Two,
stats: (
equip_time_secs: 0.01,
power: 1.0,
effect_power: 1.0,
speed: 1.0,
crit_chance: 0.0625,
range: 1.0,
energy_efficiency: 1.0,
buff_strength: 1.0,
),
)),
quality: Low,
tags: [],
ability_spec: Some(Custom("Simple Flying Melee")),
)

View File

@ -0,0 +1,30 @@
#![enable(implicit_some)]
(
head: Choice([
(1, Item("common.items.armor.misc.head.bamboo_twig")),
(1, Item("common.items.armor.misc.head.boreal_warhelm")),
(1, Item("common.items.armor.misc.head.crown")),
(1, Item("common.items.armor.misc.head.facegourd")),
(1, Item("common.items.armor.misc.head.helmet")),
(1, Item("common.items.armor.misc.head.hog_hood")),
(1, Item("common.items.armor.misc.head.hood")),
(1, Item("common.items.armor.misc.head.hood_dark")),
(1, Item("common.items.armor.misc.head.mitre")),
(1, Item("common.items.armor.misc.head.spikeguard")),
(1, Item("common.items.armor.misc.head.straw")),
(1, Item("common.items.armor.misc.head.wanderers_hat")),
(1, Item("common.items.armor.misc.head.winged_coronet")),
(1, Item("common.items.armor.misc.head.bandana.red")),
(1, Item("common.items.armor.misc.head.bandana.thief")),
]),
chest: Choice([
(1, Item("common.items.armor.leather_plate.chest")),
]),
legs: Choice([
(1, Item("common.items.armor.leather_plate.pants")),
]),
feet: Choice([
(1, Item("common.items.armor.misc.foot.sandals")),
(1, Item("common.items.armor.cloth_blue.foot")),
]),
)

View File

@ -0,0 +1,4 @@
[
(1.0, Item("common.items.food.pumpkin_spice_brew")),
(5.0, Item("common.items.food.honeycorn")),
]

View File

@ -0,0 +1,9 @@
[
// Weapons
(5.0, LootTable("common.loot_tables.weapons.tier-3")),
// Armor
(5.0, LootTable("common.loot_tables.armor.tier-3")),
// Misc
(2.0, Item("common.items.armor.misc.head.facegourd")),
(2.0, Item("common.items.lantern.pumpkin")),
]

View File

@ -0,0 +1,4 @@
[
(1.0, Item("common.items.food.pumpkin_spice_brew")),
(5.0, Item("common.items.food.honeycorn")),
]

View File

@ -0,0 +1,9 @@
[
// halloween event loot
(2.0, Item("common.items.food.pumpkin_spice_brew")),
(5.0, Item("common.items.food.honeycorn")),
// crafting
(1.0, Item("common.items.crafting_ing.hide.animal_hide")),
(1.0, Item("common.items.crafting_ing.animal_misc.sharp_fang")),
]

View File

@ -824,6 +824,10 @@
keyword: "penguin",
generic: "Penguin"
),
bat: (
keyword: "bat",
generic: "Bat"
),
)
),
biped_large: (

View File

@ -992,6 +992,18 @@
],
threshold: 0.3,
),
Inventory(Consumed("Honeycorn")): (
files: [
"voxygen.audio.sfx.inventory.consumable.food",
],
threshold: 0.3,
),
Inventory(Consumed("Pumpkin Spice Brew")): (
files: [
"voxygen.audio.sfx.inventory.consumable.liquid",
],
threshold: 0.3,
),
//
//Combat

View File

@ -2858,6 +2858,10 @@
"voxel.armor.misc.head.spikeguard",
(0.0, 0.0, 0.0), (-120.0, 210.0,15.0), 1.3,
),
Simple("common.items.armor.misc.head.facegourd"): VoxTrans(
"voxel.armor.misc.head.facegourd",
(0.0, 0.0, 0.0), (-120.0, 210.0,15.0), 1.3,
),
Simple("common.items.armor.misc.head.winged_coronet"): VoxTrans(
"voxel.armor.misc.head.winged_coronet",
(0.0, 0.0, 0.0), (-120.0, 210.0,15.0), 1.3,
@ -3129,6 +3133,14 @@
"voxel.object.sunflower_ice_tea",
(0.0, 0.0, 0.0), (-50.0, -60.0, -35.0), 0.9,
),
Simple("common.items.food.pumpkin_spice_brew"): VoxTrans(
"voxel.object.pumpkin_spice_brew",
(0.0, 0.0, 0.0), (-50.0, -60.0, -35.0), 0.9,
),
Simple("common.items.food.honeycorn"): VoxTrans(
"voxel.object.honeycorn",
(0.0, 0.0, 0.0), (-20.0, 10.0, 20.0), 0.9,
),
Simple("common.items.food.carrot"): VoxTrans(
"voxel.sprite.carrot.carrot",
(0.0, 0.0, 0.0), (-20.0, 10.0, 20.0), 0.9,

BIN
assets/voxygen/voxel/armor/misc/head/facegourd.vox (Stored with Git LFS) Normal file

Binary file not shown.

View File

@ -223,4 +223,32 @@
central: ("npc.penguin.male.tail"),
)
),
(Bat, Male): (
head: (
offset: (-4.5, 2.0, -11.0),
central: ("npc.bat.male.head"),
),
torso: (
offset: (-3.5, -5.0, -6.5),
central: ("npc.bat.male.torso"),
),
tail: (
offset: (-2.5, -1.5, -1.5),
central: ("npc.bat.male.tail"),
)
),
(Bat, Female): (
head: (
offset: (-4.5, 2.0, -11.0),
central: ("npc.bat.male.head"),
),
torso: (
offset: (-3.5, -5.0, -6.5),
central: ("npc.bat.male.torso"),
),
tail: (
offset: (-2.5, -1.5, -1.5),
central: ("npc.bat.male.tail"),
)
),
})

View File

@ -287,4 +287,40 @@
lateral: ("npc.penguin.male.leg_r"),
)
),
(Bat, Male): (
wing_l: (
offset: (-13.0, 2.5, -3.5),
lateral: ("npc.bat.male.wing_r"),
),
wing_r: (
offset: (-1.0, 2.5, -3.5),
lateral: ("npc.bat.male.wing_r"),
),
foot_l: (
offset: (-2.0, 2.0, -8.5),
lateral: ("npc.bat.male.leg_r"),
),
foot_r: (
offset: (-2.0, 2.0, -8.5),
lateral: ("npc.bat.male.leg_r"),
)
),
(Bat, Female): (
wing_l: (
offset: (-13.0, 2.5, -3.5),
lateral: ("npc.bat.male.wing_r"),
),
wing_r: (
offset: (-1.0, 2.5, -3.5),
lateral: ("npc.bat.male.wing_r"),
),
foot_l: (
offset: (-2.0, 2.0, -8.5),
lateral: ("npc.bat.male.leg_r"),
),
foot_r: (
offset: (-2.0, 2.0, -8.5),
lateral: ("npc.bat.male.leg_r"),
)
),
})

View File

@ -635,7 +635,7 @@
color: None
),
(Orc, Female, "common.items.armor.misc.head.helmet"): (
vox_spec: ("armor.misc.head.helmet", (-3.0, -3.0, -1.0)),
vox_spec: ("armor.misc.head.helmet", (-3.0, -6.0, -1.0)),
color: None
),
(Orc, Male, "common.items.armor.misc.head.helmet"): (
@ -960,6 +960,55 @@
vox_spec: ("armor.misc.head.spikeguard", (-3.0, -5.0, 7.0)),
color: None
),
//
(Human, Female, "common.items.armor.misc.head.facegourd"): (
vox_spec: ("armor.misc.head.facegourd", (-4.0, -5.0, -1.0)),
color: None
),
(Human, Male, "common.items.armor.misc.head.facegourd"): (
vox_spec: ("armor.misc.head.facegourd", (-4.0, -5.0, -1.0)),
color: None
),
(Elf, Female, "common.items.armor.misc.head.facegourd"): (
vox_spec: ("armor.misc.head.facegourd", (-3.0, -5.0, -1.0)),
color: None
),
(Elf, Male, "common.items.armor.misc.head.facegourd"): (
vox_spec: ("armor.misc.head.facegourd", (-3.0, -5.0, -1.0)),
color: None
),
(Orc, Female, "common.items.armor.misc.head.facegourd"): (
vox_spec: ("armor.misc.head.facegourd", (-3.0, -6.0, -1.0)),
color: None
),
(Orc, Male, "common.items.armor.misc.head.facegourd"): (
vox_spec: ("armor.misc.head.facegourd", (-3.0, -6.0, 2.0)),
color: None
),
(Dwarf, Female, "common.items.armor.misc.head.facegourd"): (
vox_spec: ("armor.misc.head.facegourd", (-5.0, -4.0, -1.0)),
color: None
),
(Dwarf, Male, "common.items.armor.misc.head.facegourd"): (
vox_spec: ("armor.misc.head.facegourd", (-3.0, -3.0, -1.0)),
color: None
),
(Draugr, Female, "common.items.armor.misc.head.facegourd"): (
vox_spec: ("armor.misc.head.facegourd", (-6.0, -5.0, -1.0)),
color: None
),
(Draugr, Male, "common.items.armor.misc.head.facegourd"): (
vox_spec: ("armor.misc.head.facegourd", (-6.0, -5.0, 1.0)),
color: None
),
(Danari, Female, "common.items.armor.misc.head.facegourd"): (
vox_spec: ("armor.misc.head.facegourd", (-2.0, -5.0, 1.0)),
color: None
),
(Danari, Male, "common.items.armor.misc.head.facegourd"): (
vox_spec: ("armor.misc.head.facegourd", (-2.0, -5.0, -1.0)),
color: None
),
// Merchant Turban
(Human, Male, "common.items.armor.merchant.turban"): (
vox_spec: ("armor.merchant.turban", (-4.0, -7.0, -6.0)),

View File

@ -720,6 +720,7 @@
Simple("common.items.armor.misc.head.crown"): "voxel.armor.misc.head.crown",
Simple("common.items.armor.misc.head.mitre"): "voxel.armor.misc.head.mitre",
Simple("common.items.armor.misc.head.spikeguard"): "voxel.armor.misc.head.spikeguard",
Simple("common.items.armor.misc.head.facegourd"): "voxel.armor.misc.head.facegourd",
Simple("common.items.armor.misc.head.winged_coronet"): "voxel.armor.misc.head.winged_coronet",
Simple("common.items.armor.misc.head.boreal_warhelm"): "voxel.armor.misc.head.boreal_warhelm",
Simple("common.items.calendar.christmas.armor.misc.head.woolly_wintercap"): "voxel.armor.misc.head.woolly_wintercap",
@ -793,6 +794,8 @@
Simple("common.items.food.apple_stick"): "voxel.object.apple_stick",
Simple("common.items.food.mushroom_stick"): "voxel.object.mushroom_stick",
Simple("common.items.food.sunflower_icetea"): "voxel.object.sunflower_ice_tea",
Simple("common.items.food.pumpkin_spice_brew"): "voxel.object.pumpkin_spice_brew",
Simple("common.items.food.honeycorn"): "voxel.object.honeycorn",
Simple("common.items.food.carrot"): "voxel.sprite.carrot.carrot",
Simple("common.items.food.tomato"): "voxel.sprite.tomato.tomato",
Simple("common.items.food.lettuce"): "voxel.sprite.cabbage.cabbage",

BIN
assets/voxygen/voxel/npc/bat/male/head.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/voxel/npc/bat/male/leg_r.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/voxel/npc/bat/male/tail.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/voxel/npc/bat/male/torso.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/voxel/npc/bat/male/wing_r.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/voxel/object/honeycorn.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/voxel/object/pumpkin_spice_brew.vox (Stored with Git LFS) Normal file

Binary file not shown.

View File

@ -0,0 +1,17 @@
SpawnEntry (
name: "Halloween Jungle Area",
note: "Halloween NPCs",
rules: [
Pack(
groups: [
(1, (1, 1, "common.entity.calendar.halloween.aggressive.halloween_harvester")),
(2, (1, 1, "common.entity.calendar.halloween.aggressive.halloween_dullahan")),
(3, (4, 8, "common.entity.wild.aggressive.bat")),
(3, (3, 6, "common.entity.calendar.halloween.aggressive.trickster")),
],
spawn_mode: Land,
calendar_events: Some([Halloween]),
day_period: [Night, Morning, Noon, Evening],
),
],
)

View File

@ -0,0 +1,17 @@
SpawnEntry (
name: "Halloween Taiga Core",
note: "Halloween NPCs",
rules: [
Pack(
groups: [
(1, (1, 1, "common.entity.calendar.halloween.aggressive.halloween_harvester")),
(2, (1, 1, "common.entity.calendar.halloween.aggressive.halloween_dullahan")),
(3, (4, 8, "common.entity.wild.aggressive.bat")),
(3, (3, 6, "common.entity.calendar.halloween.aggressive.trickster")),
],
spawn_mode: Land,
calendar_events: Some([Halloween]),
day_period: [Night, Morning, Noon, Evening],
),
],
)

View File

@ -0,0 +1,17 @@
SpawnEntry (
name: "Halloween Temperate Rainforest",
note: "Halloween NPCs",
rules: [
Pack(
groups: [
(1, (1, 1, "common.entity.calendar.halloween.aggressive.halloween_harvester")),
(2, (1, 1, "common.entity.calendar.halloween.aggressive.halloween_dullahan")),
(3, (4, 8, "common.entity.wild.aggressive.bat")),
(3, (3, 6, "common.entity.calendar.halloween.aggressive.trickster")),
],
spawn_mode: Land,
calendar_events: Some([Halloween]),
day_period: [Night, Morning, Noon, Evening],
),
],
)

View File

@ -0,0 +1,17 @@
SpawnEntry (
name: "Halloween Tropical Rainforest",
note: "Halloween NPCs",
rules: [
Pack(
groups: [
(1, (1, 1, "common.entity.calendar.halloween.aggressive.halloween_harvester")),
(2, (1, 1, "common.entity.calendar.halloween.aggressive.halloween_dullahan")),
(3, (4, 8, "common.entity.wild.aggressive.bat")),
(3, (3, 6, "common.entity.calendar.halloween.aggressive.trickster")),
],
spawn_mode: Land,
calendar_events: Some([Halloween]),
day_period: [Night, Morning, Noon, Evening],
),
],
)

View File

@ -0,0 +1,17 @@
SpawnEntry (
name: "Halloween Tundra Core",
note: "Halloween NPCs",
rules: [
Pack(
groups: [
(1, (1, 1, "common.entity.calendar.halloween.aggressive.halloween_harvester")),
(2, (1, 1, "common.entity.calendar.halloween.aggressive.halloween_dullahan")),
(3, (4, 8, "common.entity.wild.aggressive.bat")),
(3, (3, 6, "common.entity.calendar.halloween.aggressive.trickster")),
],
spawn_mode: Land,
calendar_events: Some([Halloween]),
day_period: [Night, Morning, Noon, Evening],
),
],
)

View File

@ -0,0 +1,17 @@
SpawnEntry (
name: "Halloween Tundra Forest",
note: "Halloween NPCs",
rules: [
Pack(
groups: [
(1, (1, 1, "common.entity.calendar.halloween.aggressive.halloween_harvester")),
(2, (1, 1, "common.entity.calendar.halloween.aggressive.halloween_dullahan")),
(3, (4, 8, "common.entity.wild.aggressive.bat")),
(3, (3, 6, "common.entity.calendar.halloween.aggressive.trickster")),
],
spawn_mode: Land,
calendar_events: Some([Halloween]),
day_period: [Night, Morning, Noon, Evening],
),
],
)

View File

@ -0,0 +1,17 @@
SpawnEntry (
name: "Halloween Tundra Snow",
note: "Halloween NPCs",
rules: [
Pack(
groups: [
(1, (1, 1, "common.entity.calendar.halloween.aggressive.halloween_harvester")),
(2, (1, 1, "common.entity.calendar.halloween.aggressive.halloween_dullahan")),
(3, (4, 8, "common.entity.wild.aggressive.bat")),
(3, (3, 6, "common.entity.calendar.halloween.aggressive.trickster")),
],
spawn_mode: Land,
calendar_events: Some([Halloween]),
day_period: [Night, Morning, Noon, Evening],
),
],
)

View File

@ -20,6 +20,7 @@ SpawnEntry (
(1, (1, 1, "common.entity.wild.peaceful.holladon")),
(1, (1, 1, "common.entity.wild.peaceful.porcupine")),
(1, (1, 1, "common.entity.wild.peaceful.pangolin")),
(1, (4, 8, "common.entity.wild.aggressive.bat")),
],
spawn_mode: Land,
day_period: [Night],

View File

@ -22,6 +22,7 @@ SpawnEntry (
groups: [
(5, (1, 1, "common.entity.wild.peaceful.quokka")),
(1, (1, 1, "common.entity.wild.peaceful.tortoise")),
(5, (4, 8, "common.entity.wild.aggressive.bat")),
],
spawn_mode: Land,
day_period: [Night],

View File

@ -45,6 +45,7 @@ SpawnEntry (
// Pack
(5, (1, 3, "common.entity.wild.peaceful.rat")),
(5, (1, 3, "common.entity.wild.peaceful.squirrel")),
(10, (4, 8, "common.entity.wild.aggressive.bat")),
],
spawn_mode: Land,
day_period: [Night],

View File

@ -6,6 +6,7 @@ use serde::{Deserialize, Serialize};
#[repr(u16)]
pub enum CalendarEvent {
Christmas = 0,
Halloween = 1,
}
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
@ -37,6 +38,10 @@ impl Calendar {
this.events.push(CalendarEvent::Christmas);
}
if now.month() == 10 && (24..=31).contains(&now.day()) {
this.events.push(CalendarEvent::Halloween);
}
this
}
}

View File

@ -310,6 +310,7 @@ impl<'a> From<&'a Body> for Psyche {
bird_medium::Species::Peacock => 0.4,
bird_medium::Species::Eagle => 0.3,
bird_medium::Species::Parrot => 0.8,
bird_medium::Species::Bat => 0.0,
_ => 0.5,
},
Body::BirdLarge(_) => 0.1,

View File

@ -27,7 +27,7 @@ use specs::{Component, DerefFlaggedStorage};
use strum::Display;
use vek::*;
use super::{BuffKind, Collider, Density, Mass};
use super::{BuffKind, Collider, Density, Mass, Scale};
make_case_elim!(
body,
@ -231,6 +231,17 @@ impl Body {
)
}
pub fn scale(&self) -> Scale {
let s = match self {
Body::BirdMedium(bird_medium) => match bird_medium.species {
bird_medium::Species::Bat => 0.5,
_ => 1.0,
},
_ => 1.0,
};
Scale(s)
}
/// Average density of the body
// Units are based on kg/m³
pub fn density(&self) -> Density {
@ -280,6 +291,7 @@ impl Body {
bird_medium::Species::Parrot => 2.0,
bird_medium::Species::Penguin => 8.0,
bird_medium::Species::Peacock => 5.0,
bird_medium::Species::Bat => 2.0,
},
Body::BirdLarge(_) => 100.0,
@ -412,6 +424,7 @@ impl Body {
bird_medium::Species::Duck => Vec3::new(0.9, 1.0, 1.4),
bird_medium::Species::Goose => Vec3::new(1.0, 1.2, 1.5),
bird_medium::Species::Peacock => Vec3::new(1.3, 1.1, 1.4),
bird_medium::Species::Bat => Vec3::new(2.0, 2.0, 1.5),
_ => Vec3::new(2.0, 1.0, 1.5),
},
Body::BirdLarge(body) => match body.species {
@ -702,6 +715,7 @@ impl Body {
bird_medium::Species::Eagle => 45,
bird_medium::Species::Owl => 45,
bird_medium::Species::Duck => 10,
bird_medium::Species::Bat => 20,
_ => 15,
},
Body::FishMedium(_) => 15,

View File

@ -45,6 +45,7 @@ make_case_elim!(
Owl = 5,
Parrot = 6,
Penguin = 7,
Bat = 8,
}
);
@ -61,6 +62,7 @@ pub struct AllSpecies<SpeciesMeta> {
pub owl: SpeciesMeta,
pub parrot: SpeciesMeta,
pub penguin: SpeciesMeta,
pub bat: SpeciesMeta,
}
impl<'a, SpeciesMeta> core::ops::Index<&'a Species> for AllSpecies<SpeciesMeta> {
@ -77,11 +79,12 @@ impl<'a, SpeciesMeta> core::ops::Index<&'a Species> for AllSpecies<SpeciesMeta>
Species::Owl => &self.owl,
Species::Parrot => &self.parrot,
Species::Penguin => &self.penguin,
Species::Bat => &self.bat,
}
}
}
pub const ALL_SPECIES: [Species; 8] = [
pub const ALL_SPECIES: [Species; 9] = [
Species::Duck,
Species::Chicken,
Species::Goose,
@ -90,6 +93,7 @@ pub const ALL_SPECIES: [Species; 8] = [
Species::Owl,
Species::Parrot,
Species::Penguin,
Species::Bat,
];
impl<'a, SpeciesMeta: 'a> IntoIterator for &'a AllSpecies<SpeciesMeta> {

View File

@ -1,7 +1,7 @@
use crate::{
assets::{self, AssetExt},
comp::{
arthropod, biped_large, biped_small, bird_large, golem,
arthropod, biped_large, biped_small, bird_large, bird_medium, golem,
inventory::{
loadout::Loadout,
slot::{ArmorSlot, EquipSlot},
@ -778,6 +778,12 @@ fn default_main_tool(body: &Body) -> Item {
"common.items.npc_weapons.unique.birdlargebasic",
)),
},
Body::BirdMedium(bird_medium) => match bird_medium.species {
bird_medium::Species::Bat => Some(Item::new_from_asset_expect(
"common.items.npc_weapons.unique.simpleflyingbasic",
)),
_ => None,
},
_ => None,
};

View File

@ -325,6 +325,43 @@ impl<'a> AgentData<'a> {
}
}
} else {
// Bats should fly
// Use a proportional controller as the bouncing effect mimics bat flight
if self.traversal_config.can_fly
&& self
.inventory
.equipped(EquipSlot::ActiveMainhand)
.as_ref()
.map_or(false, |item| {
item.ability_spec().map_or(false, |a_s| match &*a_s {
AbilitySpec::Custom(spec) => {
matches!(spec.as_str(), "Simple Flying Melee")
},
_ => false,
})
})
{
// Bats don't like the ground, so make sure they are always flying
controller.push_basic_input(InputKind::Fly);
if read_data
.terrain
.ray(self.pos.0, self.pos.0 - (Vec3::unit_z() * 5.0))
.until(Block::is_solid)
.cast()
.1
.map_or(true, |b| b.is_some())
{
// Fly up
controller.inputs.move_z = 1.0;
// If on the ground, jump
if self.physics_state.on_ground.is_some() {
controller.push_basic_input(InputKind::Jump);
}
} else {
// Fly down
controller.inputs.move_z = -1.0;
}
}
agent.bearing += Vec2::new(rng.gen::<f32>() - 0.5, rng.gen::<f32>() - 0.5) * 0.1
- agent.bearing * 0.003
- agent.patrol_origin.map_or(Vec2::zero(), |patrol_origin| {
@ -774,6 +811,7 @@ impl<'a> AgentData<'a> {
AbilitySpec::Custom(spec) => match spec.as_str() {
"Oni" | "Sword Simple" => Tactic::Sword,
"Staff Simple" => Tactic::Staff,
"Simple Flying Melee" => Tactic::SimpleFlyingMelee,
"Bow Simple" => Tactic::Bow,
"Stone Golem" => Tactic::StoneGolem,
"Quad Med Quick" => Tactic::CircleCharge {
@ -1006,6 +1044,14 @@ impl<'a> AgentData<'a> {
// Match on tactic. Each tactic has different controls depending on the distance
// from the agent to the target.
match tactic {
Tactic::SimpleFlyingMelee => self.handle_simple_flying_melee(
agent,
controller,
&attack_data,
tgt_data,
read_data,
rng,
),
Tactic::SimpleMelee => {
self.handle_simple_melee(agent, controller, &attack_data, tgt_data, read_data, rng)
},

View File

@ -53,6 +53,64 @@ impl<'a> AgentData<'a> {
}
}
// Intended for any agent that has one attack, that attack is a melee attack,
// and the agent is able to freely fly around
pub fn handle_simple_flying_melee(
&self,
_agent: &mut Agent,
controller: &mut Controller,
attack_data: &AttackData,
tgt_data: &TargetData,
read_data: &ReadData,
_rng: &mut impl Rng,
) {
// Fly to target
let dir_to_target = ((tgt_data.pos.0 + Vec3::unit_z() * 1.5) - self.pos.0)
.try_normalized()
.unwrap_or_else(Vec3::zero);
let speed = 1.0;
controller.inputs.move_dir = dir_to_target.xy() * speed;
// Always fly! If the floor can't touch you, it can't hurt you...
controller.push_basic_input(InputKind::Fly);
// Flee from the ground! The internet told me it was lava!
// If on the ground, jump with every last ounce of energy, holding onto all that
// is dear in life and straining for the wide open skies.
if self.physics_state.on_ground.is_some() {
controller.push_basic_input(InputKind::Jump);
} else {
// Only fly down if close enough to target in the xy plane
// Otherwise fly towards the target bouncing around a 5 block altitude
let mut maintain_altitude = |altitude| {
if read_data
.terrain
.ray(self.pos.0, self.pos.0 - (Vec3::unit_z() * altitude))
.until(Block::is_solid)
.cast()
.1
.map_or(true, |b| b.is_some())
{
// Fly up
controller.inputs.move_z = 1.0;
} else {
// Fly down
controller.inputs.move_z = -1.0;
}
};
if (tgt_data.pos.0 - self.pos.0).xy().magnitude_squared() > (5.0_f32).powi(2) {
// If above 5 blocks, fly down
maintain_altitude(5.0);
} else {
maintain_altitude(2.0);
// Attack if in range
if attack_data.dist_sqrd < 3.5_f32.powi(2) && attack_data.angle < 150.0 {
controller.push_basic_input(InputKind::Primary);
}
}
}
}
// Intended for any agent that has one attack, that attack is a melee attack,
// the agent is able to freely walk around, and the agent is trying to attack
// from behind its target

View File

@ -74,6 +74,7 @@ impl AttackData {
pub enum Tactic {
// General tactics
SimpleMelee,
SimpleFlyingMelee,
SimpleBackstab,
ElevatedRanged,
Turret,

View File

@ -1212,6 +1212,7 @@ fn handle_spawn(
body,
)
.with(comp::Vel(vel))
.with(body.scale())
.with(alignment);
if ai {

View File

@ -114,6 +114,7 @@ impl<'a> From<&'a Body> for SkeletonAttr {
(Owl, Female) => (2.5, 7.0),
(Parrot, _) => (0.5, 4.5),
(Penguin, _) => (1.5, 6.0),
(Bat, _) => (2.5, 5.0),
},
chest: match (body.species, body.body_type) {
(Duck, _) => (0.0, 6.0),
@ -126,6 +127,7 @@ impl<'a> From<&'a Body> for SkeletonAttr {
(Owl, Female) => (0.0, 4.5),
(Parrot, _) => (0.0, 5.0),
(Penguin, _) => (0.0, 8.0),
(Bat, _) => (0.0, 8.0),
},
tail: match (body.species, body.body_type) {
(Duck, _) => (-5.0, 1.0),
@ -138,6 +140,7 @@ impl<'a> From<&'a Body> for SkeletonAttr {
(Owl, Female) => (-6.0, -2.5),
(Parrot, _) => (-8.0, -2.0),
(Penguin, _) => (-3.0, -4.0),
(Bat, _) => (-8.0, -4.0),
},
wing: match (body.species, body.body_type) {
(Duck, _) => (3.5, -0.5, 2.0),
@ -150,6 +153,7 @@ impl<'a> From<&'a Body> for SkeletonAttr {
(Owl, Female) => (3.5, -6.0, 3.5),
(Parrot, _) => (2.0, -4.5, 3.0),
(Penguin, _) => (4.0, 0.5, 1.0),
(Bat, _) => (3.0, -8.0, -1.0),
},
foot: match (body.species, body.body_type) {
(Duck, _) => (2.5, -2.0, 4.0),
@ -162,6 +166,7 @@ impl<'a> From<&'a Body> for SkeletonAttr {
(Owl, Female) => (1.5, -3.0, 6.5),
(Parrot, _) => (1.5, -3.0, 3.0),
(Penguin, _) => (2.5, -2.0, 6.0),
(Bat, _) => (5.0, -1.0, 8.0),
},
feed: match (body.species, body.body_type) {
(Chicken, _) => 1.2,

View File

@ -265,6 +265,19 @@ pub fn block_from_structure(
&& field.chance(pos + structure_pos, 0.025)
{
Block::new(BlockKind::GlowingWeakRock, Rgb::new(255, 0, 0))
} else if calendar.map_or(false, |c| c.is_event(CalendarEvent::Halloween))
&& sblock != StructureBlock::PineLeaves
{
let (c0, c1) = match structure_seed % 6 {
0 => (Rgb::new(165.0, 150.0, 11.0), Rgb::new(170.0, 165.0, 16.0)),
1 | 2 => (Rgb::new(218.0, 53.0, 3.0), Rgb::new(226.0, 62.0, 5.0)),
_ => (Rgb::new(230.0, 120.0, 20.0), Rgb::new(242.0, 130.0, 25.0)),
};
Block::new(
BlockKind::Leaves,
Rgb::<f32>::lerp(c0, c1, lerp).map(|e| e as u8),
)
} else {
Block::new(
BlockKind::Leaves,

View File

@ -828,6 +828,10 @@ fn apply_entity_spawns<R: Rng>(canvas: &mut Canvas, wpos: Vec3<i32>, biome: &Bio
Some("common.entity.wild.peaceful.fungome"),
(biome.mushroom + 0.02) * 0.5,
),
(
Some("common.entity.wild.aggressive.bat"),
(biome.mushroom + 0.1) * 0.5,
),
// Leafy biome
(
Some("common.entity.wild.peaceful.holladon"),
@ -869,6 +873,10 @@ fn apply_entity_spawns<R: Rng>(canvas: &mut Canvas, wpos: Vec3<i32>, biome: &Bio
Some("common.entity.wild.aggressive.swamp_troll"),
(biome.leafy + 0.0) * 0.1,
),
(
Some("common.entity.wild.aggressive.bat"),
(biome.leafy + 0.1) * 0.5,
),
// Dusty biome
(
Some("common.entity.wild.aggressive.dodarock"),
@ -890,6 +898,10 @@ fn apply_entity_spawns<R: Rng>(canvas: &mut Canvas, wpos: Vec3<i32>, biome: &Bio
Some("common.entity.wild.peaceful.rat"),
(biome.dusty + 0.1) * 0.3,
),
(
Some("common.entity.wild.aggressive.bat"),
(biome.dusty + 0.1) * 0.5,
),
// Icy biome
(
Some("common.entity.wild.aggressive.blue_oni"),

View File

@ -1,5 +1,8 @@
use crate::{column::ColumnSample, sim::SimChunk, Canvas, CONFIG};
use common::terrain::{Block, BlockKind, SpriteKind};
use common::{
calendar::{Calendar, CalendarEvent},
terrain::{Block, BlockKind, SpriteKind},
};
use noise::NoiseFn;
use num::traits::Pow;
use rand::prelude::*;
@ -27,7 +30,7 @@ pub fn density_factor_by_altitude(lower_limit: f32, altitude: f32, upper_limit:
const MUSH_FACT: f32 = 1.0e-4; // To balance things around the mushroom spawning rate
const GRASS_FACT: f32 = 1.0e-3; // To balance things around the grass spawning rate
const DEPTH_WATER_NORM: f32 = 15.0; // Water depth at which regular underwater sprites start spawning
pub fn apply_scatter_to(canvas: &mut Canvas, rng: &mut impl Rng) {
pub fn apply_scatter_to(canvas: &mut Canvas, rng: &mut impl Rng, calendar: Option<&Calendar>) {
enum WaterMode {
Underwater,
Floating,
@ -283,16 +286,22 @@ pub fn apply_scatter_to(canvas: &mut Canvas, rng: &mut impl Rng) {
kind: Pumpkin,
water_mode: Ground,
permit: |b| matches!(b, BlockKind::Grass),
f: |_, col| {
(
close(col.temp, CONFIG.temperate_temp, 0.5).min(close(
col.humidity,
CONFIG.forest_hum,
0.5,
)) * MUSH_FACT
* 500.0,
Some((0.0, 512.0, 0.05)),
)
f: if calendar.map_or(false, |calendar| {
calendar.is_event(CalendarEvent::Halloween)
}) {
|_, _| (0.1, Some((0.0003, 128.0, 0.1)))
} else {
|_, col| {
(
close(col.temp, CONFIG.temperate_temp, 0.5).min(close(
col.humidity,
CONFIG.forest_hum,
0.5,
)) * MUSH_FACT
* 500.0,
Some((0.0, 512.0, 0.05)),
)
}
},
},
// Collectable Objects

View File

@ -167,6 +167,10 @@ pub fn spawn_manifest() -> Vec<(&'static str, DensityFn)> {
"world.wildlife.spawn.calendar.christmas.tundra.core",
|c, _col| close(c.temp, CONFIG.snow_temp, 0.15) * BASE_DENSITY * 0.5,
),
(
"world.wildlife.spawn.calendar.halloween.tundra.core",
|c, _col| close(c.temp, CONFIG.snow_temp, 0.15) * BASE_DENSITY * 0.5,
),
// Snowy animals
("world.wildlife.spawn.tundra.snow", |c, col| {
close(c.temp, CONFIG.snow_temp, 0.3) * BASE_DENSITY * col.snow_cover as i32 as f32 * 1.0
@ -181,6 +185,15 @@ pub fn spawn_manifest() -> Vec<(&'static str, DensityFn)> {
* 1.0
},
),
(
"world.wildlife.spawn.calendar.halloween.tundra.snow",
|c, col| {
close(c.temp, CONFIG.snow_temp, 0.3)
* BASE_DENSITY
* col.snow_cover as i32 as f32
* 1.0
},
),
// Forest animals
("world.wildlife.spawn.tundra.forest", |c, col| {
close(c.temp, CONFIG.snow_temp, 0.3) * col.tree_density * BASE_DENSITY * 1.4
@ -190,6 +203,10 @@ pub fn spawn_manifest() -> Vec<(&'static str, DensityFn)> {
"world.wildlife.spawn.calendar.christmas.tundra.forest",
|c, col| close(c.temp, CONFIG.snow_temp, 0.3) * col.tree_density * BASE_DENSITY * 1.4,
),
(
"world.wildlife.spawn.calendar.halloween.tundra.forest",
|c, col| close(c.temp, CONFIG.snow_temp, 0.3) * col.tree_density * BASE_DENSITY * 1.4,
),
// **Taiga**
// Forest core animals
("world.wildlife.spawn.taiga.core_forest", |c, col| {
@ -202,6 +219,12 @@ pub fn spawn_manifest() -> Vec<(&'static str, DensityFn)> {
close(c.temp, CONFIG.snow_temp + 0.2, 0.2) * col.tree_density * BASE_DENSITY * 0.4
},
),
(
"world.wildlife.spawn.calendar.halloween.taiga.core",
|c, col| {
close(c.temp, CONFIG.snow_temp + 0.2, 0.2) * col.tree_density * BASE_DENSITY * 0.4
},
),
// Core animals
("world.wildlife.spawn.taiga.core", |c, _col| {
close(c.temp, CONFIG.snow_temp + 0.2, 0.2) * BASE_DENSITY * 1.0
@ -243,6 +266,16 @@ pub fn spawn_manifest() -> Vec<(&'static str, DensityFn)> {
* BASE_DENSITY
* 4.0
}),
// Temperate Rainforest animals event
(
"world.wildlife.spawn.calendar.halloween.temperate.rainforest",
|c, _col| {
close(c.temp, CONFIG.temperate_temp + 0.1, 0.6)
* close(c.humidity, CONFIG.forest_hum, 0.6)
* BASE_DENSITY
* 4.0
},
),
// Water animals
("world.wildlife.spawn.temperate.water", |c, col| {
close(c.temp, CONFIG.temperate_temp, 1.0) * col.tree_density * BASE_DENSITY * 5.0
@ -262,6 +295,16 @@ pub fn spawn_manifest() -> Vec<(&'static str, DensityFn)> {
* BASE_DENSITY
* 8.0
}),
// Jungle animals event
(
"world.wildlife.spawn.calendar.halloween.jungle.area",
|c, _col| {
close(c.temp, CONFIG.tropical_temp + 0.2, 0.3)
* close(c.humidity, CONFIG.jungle_hum, 0.2)
* BASE_DENSITY
* 8.0
},
),
// **Tropical**
// Rare river animals
("world.wildlife.spawn.tropical.river_rare", |_c, col| {
@ -308,6 +351,16 @@ pub fn spawn_manifest() -> Vec<(&'static str, DensityFn)> {
* BASE_DENSITY
* 2.0
}),
// Tropical Rainforest animals event
(
"world.wildlife.spawn.calendar.halloween.tropical.rainforest",
|c, _col| {
close(c.temp, CONFIG.tropical_temp + 0.1, 0.4)
* close(c.humidity, CONFIG.desert_hum, 0.4)
* BASE_DENSITY
* 2.0
},
),
// Rock animals
("world.wildlife.spawn.tropical.rock", |c, col| {
close(c.temp, CONFIG.tropical_temp + 0.1, 0.5) * col.rock_density * BASE_DENSITY * 5.0

View File

@ -375,7 +375,7 @@ impl World {
layer::apply_trees_to(&mut canvas, &mut dynamic_rng, calendar);
}
if index.features.scatter {
layer::apply_scatter_to(&mut canvas, &mut dynamic_rng);
layer::apply_scatter_to(&mut canvas, &mut dynamic_rng, calendar);
}
if index.features.paths {
layer::apply_paths_to(&mut canvas);