mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Merge branch 'christmas' into 'master'
Christmas See merge request veloren/veloren!3047
This commit is contained in:
commit
824a978cdb
83
Cargo.lock
generated
83
Cargo.lock
generated
@ -565,6 +565,29 @@ dependencies = [
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "chrono-tz"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "58549f1842da3080ce63002102d5bc954c7bc843d4f47818e642abdc36253552"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"chrono-tz-build",
|
||||
"phf",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "chrono-tz-build"
|
||||
version = "0.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "db058d493fb2f65f41861bfed7e3fe6335264a9f0f92710cab5bdf01fef09069"
|
||||
dependencies = [
|
||||
"parse-zoneinfo",
|
||||
"phf",
|
||||
"phf_codegen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "chumsky"
|
||||
version = "0.3.2"
|
||||
@ -3896,6 +3919,15 @@ dependencies = [
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parse-zoneinfo"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c705f256449c60da65e11ff6626e0c16a0a0b96aaa348de61376b249bc340f41"
|
||||
dependencies = [
|
||||
"regex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pdqselect"
|
||||
version = "0.1.0"
|
||||
@ -3944,6 +3976,45 @@ dependencies = [
|
||||
"indexmap",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9fc3db1018c4b59d7d582a739436478b6035138b6aecbce989fc91c3e98409f"
|
||||
dependencies = [
|
||||
"phf_shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_codegen"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4fb1c3a8bc4dd4e5cfce29b44ffc14bedd2ee294559a294e2a4d4c9e9a6a13cd"
|
||||
dependencies = [
|
||||
"phf_generator",
|
||||
"phf_shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_generator"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6"
|
||||
dependencies = [
|
||||
"phf_shared",
|
||||
"rand 0.8.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_shared"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096"
|
||||
dependencies = [
|
||||
"siphasher",
|
||||
"uncased",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.7"
|
||||
@ -5918,6 +5989,15 @@ version = "1.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b63708a265f51345575b27fe43f9500ad611579e764c79edbc2037b1121959ec"
|
||||
|
||||
[[package]]
|
||||
name = "uncased"
|
||||
version = "0.9.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5baeed7327e25054889b9bd4f975f32e5f4c5d434042d59ab6cd4142c0a76ed0"
|
||||
dependencies = [
|
||||
"version_check 0.9.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-bidi"
|
||||
version = "0.3.7"
|
||||
@ -6064,6 +6144,8 @@ version = "0.10.0"
|
||||
dependencies = [
|
||||
"approx 0.4.0",
|
||||
"bitflags",
|
||||
"chrono",
|
||||
"chrono-tz",
|
||||
"clap",
|
||||
"criterion",
|
||||
"crossbeam-channel",
|
||||
@ -6292,6 +6374,7 @@ dependencies = [
|
||||
"authc",
|
||||
"bincode",
|
||||
"chrono",
|
||||
"chrono-tz",
|
||||
"crossbeam-channel",
|
||||
"futures-util",
|
||||
"hashbrown 0.11.2",
|
||||
|
11
assets/common/entity/calendar/christmas/aggressive/yeti.ron
Normal file
11
assets/common/entity/calendar/christmas/aggressive/yeti.ron
Normal file
@ -0,0 +1,11 @@
|
||||
EntityConfig (
|
||||
name: Name("Yeti"),
|
||||
body: RandomWith("yeti"),
|
||||
alignment: Alignment(Enemy),
|
||||
|
||||
loot: LootTable("common.loot_tables.calendar.christmas.yeti"),
|
||||
|
||||
hands: Uninit,
|
||||
|
||||
meta: [],
|
||||
)
|
15
assets/common/items/armor/misc/head/boreal_warhelm.ron
Normal file
15
assets/common/items/armor/misc/head/boreal_warhelm.ron
Normal file
@ -0,0 +1,15 @@
|
||||
ItemDef(
|
||||
name: "Boreal Warhelmet",
|
||||
description: "I wonder where it's pointing...",
|
||||
kind: Armor((
|
||||
kind: Head("BorealWarhelm"),
|
||||
stats: (
|
||||
protection: Some(Normal(9.0)),
|
||||
poise_resilience: Some(Normal(3.0)),
|
||||
energy_max: Some(4.0),
|
||||
energy_reward: Some(0.01),
|
||||
),
|
||||
)),
|
||||
quality: High,
|
||||
tags: [],
|
||||
)
|
@ -1,11 +1,15 @@
|
||||
ItemDef(
|
||||
name: "Dark Hood",
|
||||
description: "yep.",
|
||||
description: "Tis a bit thicker.",
|
||||
kind: Armor((
|
||||
kind: Head("DarkHood"),
|
||||
stats: (
|
||||
protection: Some(Normal(7.0)),
|
||||
poise_resilience: Some(Normal(1.0)),
|
||||
crit_power: Some(0.02),
|
||||
stealth: Some(0.1),
|
||||
),
|
||||
)),
|
||||
quality: Common,
|
||||
quality: Moderate,
|
||||
tags: [],
|
||||
)
|
||||
|
@ -6,8 +6,8 @@ ItemDef(
|
||||
stats: (
|
||||
protection: Some(Normal(4.0)),
|
||||
energy_max: Some(8.0),
|
||||
energy_reward: Some(0.12),
|
||||
crit_power: Some(0.1),
|
||||
energy_reward: Some(0.1),
|
||||
crit_power: Some(0.01),
|
||||
),
|
||||
)),
|
||||
quality: High,
|
||||
|
@ -4,10 +4,12 @@ ItemDef(
|
||||
kind: Armor((
|
||||
kind: Head("Spikeguard"),
|
||||
stats: (
|
||||
protection: Some(Normal(2.0)),
|
||||
poise_resilience: Some(Normal(10.0)),
|
||||
protection: Some(Normal(9.0)),
|
||||
poise_resilience: Some(Normal(3.0)),
|
||||
crit_power: Some(0.01),
|
||||
stealth: Some(-0.5),
|
||||
),
|
||||
)),
|
||||
quality: Common,
|
||||
quality: High,
|
||||
tags: [],
|
||||
)
|
||||
|
17
assets/common/items/armor/misc/head/winged_coronet.ron
Normal file
17
assets/common/items/armor/misc/head/winged_coronet.ron
Normal file
@ -0,0 +1,17 @@
|
||||
ItemDef(
|
||||
name: "Winged Coronet",
|
||||
description: "You feel more connected with nature.",
|
||||
kind: Armor((
|
||||
kind: Head("WingedCoronet"),
|
||||
stats: (
|
||||
protection: Some(Normal(2.0)),
|
||||
poise_resilience: Some(Normal(0.0)),
|
||||
energy_max: Some(8.0),
|
||||
energy_reward: Some(0.05),
|
||||
crit_power: Some(0.015),
|
||||
stealth: Some(0.5),
|
||||
),
|
||||
)),
|
||||
quality: High,
|
||||
tags: [],
|
||||
)
|
@ -0,0 +1,15 @@
|
||||
ItemDef(
|
||||
name: "Woolly Wintercap",
|
||||
description: "Simple, stylish, and festive.",
|
||||
kind: Armor((
|
||||
kind: Head("WoollyWintercap"),
|
||||
stats: (
|
||||
protection: Some(Normal(0.0)),
|
||||
poise_resilience: Some(Normal(0.0)),
|
||||
energy_max: Some(4.0),
|
||||
energy_reward: Some(0.04),
|
||||
),
|
||||
)),
|
||||
quality: Common,
|
||||
tags: [],
|
||||
)
|
22
assets/common/items/food/blue_cheese.ron
Normal file
22
assets/common/items/food/blue_cheese.ron
Normal file
@ -0,0 +1,22 @@
|
||||
ItemDef(
|
||||
name: "Blue Cheese",
|
||||
description: "Pungent and filling",
|
||||
kind: Consumable(
|
||||
kind: Food,
|
||||
effects: [
|
||||
Buff((
|
||||
kind: Saturation,
|
||||
data: (
|
||||
strength: 3.0,
|
||||
duration: Some((
|
||||
secs: 20,
|
||||
nanos: 0,
|
||||
)),
|
||||
),
|
||||
cat_ids: [Natural],
|
||||
)),
|
||||
]
|
||||
),
|
||||
quality: High,
|
||||
tags: [Food],
|
||||
)
|
14
assets/common/items/lantern/polaris.ron
Normal file
14
assets/common/items/lantern/polaris.ron
Normal file
@ -0,0 +1,14 @@
|
||||
ItemDef(
|
||||
name: "Polaris",
|
||||
description: "Christmas Lantern.",
|
||||
kind: Lantern(
|
||||
(
|
||||
kind: "PolarisLantern",
|
||||
color: (r: 67, g: 170, b: 255),
|
||||
strength_thousandths: 8000,
|
||||
flicker_thousandths: 600,
|
||||
),
|
||||
),
|
||||
quality: High,
|
||||
tags: [Utility],
|
||||
)
|
@ -8,6 +8,8 @@
|
||||
Armor(Head): Choice([
|
||||
(1.0, Some(Item("common.items.armor.misc.head.bandana.thief"))),
|
||||
(1.0, Some(Item("common.items.armor.misc.head.bandana.red"))),
|
||||
(1.0, Some(Item("common.items.armor.misc.head.hood"))),
|
||||
(1.0, Some(Item("common.items.armor.misc.head.hood_dark"))),
|
||||
|
||||
]),
|
||||
Lantern: Choice([
|
||||
|
@ -5,10 +5,20 @@
|
||||
Armor(Hands): Item("common.items.armor.leather_plate.hand"),
|
||||
Armor(Legs): Item("common.items.armor.leather_plate.pants"),
|
||||
Armor(Feet): Item("common.items.armor.leather_plate.foot"),
|
||||
//Armor(Head): Item("common.items.armor.leather_plate.helmet"),
|
||||
Armor(Head): Choice([
|
||||
// Christmas event
|
||||
(2.0, Some(Item("common.items.armor.misc.head.boreal_warhelm"))),
|
||||
(1.0, Some(Item("common.items.calendar.christmas.armor.misc.head.woolly_wintercap"))),
|
||||
//(4.0, Some(Item("common.items.armor.leather_plate.helmet"))),
|
||||
]),
|
||||
|
||||
Lantern: Choice([
|
||||
(1.0, Some(Item("common.items.lantern.black_0"))),
|
||||
(2.0, None),
|
||||
//(1.0, Some(Item("common.items.lantern.black_0"))),
|
||||
//(2.0, None),
|
||||
// Christmas event
|
||||
(1.0, Some(Item("common.items.lantern.blue_0"))),
|
||||
(1.0, Some(Item("common.items.lantern.polaris"))),
|
||||
(2.0, Some(Item("common.items.lantern.red_0"))),
|
||||
(2.0, Some(Item("common.items.lantern.green_0"))),
|
||||
]),
|
||||
})
|
||||
|
@ -20,7 +20,9 @@
|
||||
(1.0, Some(Item("common.items.armor.cloth_blue.foot"))),
|
||||
]),
|
||||
Armor(Head): Choice([
|
||||
(1.0, Some(Item("common.items.armor.misc.head.straw"))),
|
||||
(1.0, None),
|
||||
// Christmas event
|
||||
(1.0, Some(Item("common.items.calendar.christmas.armor.misc.head.woolly_wintercap"))),
|
||||
//(1.0, Some(Item("common.items.armor.misc.head.straw"))),
|
||||
//(2.0, None),
|
||||
]),
|
||||
})
|
||||
|
6
assets/common/loot_tables/calendar/christmas/boss.ron
Normal file
6
assets/common/loot_tables/calendar/christmas/boss.ron
Normal file
@ -0,0 +1,6 @@
|
||||
[
|
||||
(4.0, ItemQuantity("common.items.food.blue_cheese", 1, 30)),
|
||||
(3.0, Item("common.items.calendar.christmas.armor.misc.head.woolly_wintercap")),
|
||||
(2.0, Item("common.items.armor.misc.head.boreal_warhelm")),
|
||||
(1.0, Item("common.items.lantern.polaris")),
|
||||
]
|
5
assets/common/loot_tables/calendar/christmas/yeti.ron
Normal file
5
assets/common/loot_tables/calendar/christmas/yeti.ron
Normal file
@ -0,0 +1,5 @@
|
||||
[
|
||||
(5.0, ItemQuantity("common.items.food.blue_cheese", 1, 10)),
|
||||
(6.0, ItemQuantity("common.items.mineral.ore.coal", 1, 3)),
|
||||
(1.0, ItemQuantity("common.items.mineral.gem.diamond", 1, 1)),
|
||||
]
|
@ -12,7 +12,8 @@
|
||||
// Armor
|
||||
(0.01, Item("common.items.armor.misc.head.bandana.thief")),
|
||||
(0.01, Item("common.items.armor.misc.head.bandana.red")),
|
||||
(0.005, Item("common.items.armor.misc.head.hood")),
|
||||
(0.01, Item("common.items.armor.misc.head.hood")),
|
||||
(0.01, Item("common.items.armor.misc.head.hood_dark")),
|
||||
// Food
|
||||
(1.0, LootTable("common.loot_tables.food.wild_ingredients")),
|
||||
(0.25, LootTable("common.loot_tables.food.prepared")),
|
||||
|
@ -296,6 +296,14 @@
|
||||
],
|
||||
craft_sprite: Some(Forge),
|
||||
),
|
||||
// Only for Christmas event so remove once done
|
||||
"diamonds": (
|
||||
output: ("common.items.mineral.gem.diamond", 1),
|
||||
inputs: [
|
||||
(Item("common.items.mineral.ore.coal"), 20),
|
||||
],
|
||||
craft_sprite: Some(Forge),
|
||||
),
|
||||
"cotton": (
|
||||
output: ("common.items.crafting_ing.cloth.cotton", 1),
|
||||
inputs: [
|
||||
@ -1889,6 +1897,15 @@
|
||||
],
|
||||
craft_sprite: Some(CraftingBench),
|
||||
),
|
||||
"winged coronet": (
|
||||
output: ("common.items.armor.misc.head.winged_coronet", 1),
|
||||
inputs: [
|
||||
(Item("common.items.mineral.gem.emerald"), 1),
|
||||
(Item("common.items.mineral.ingot.gold"), 4),
|
||||
(Item("common.items.crafting_ing.animal_misc.raptor_feather"), 2),
|
||||
],
|
||||
craft_sprite: Some(CraftingBench),
|
||||
),
|
||||
//"metal_blade": (
|
||||
// output: ("common.items.crafting_ing.modular.damage.sword.metal_blade", 1),
|
||||
// inputs: [
|
||||
|
@ -1053,6 +1053,10 @@
|
||||
"voxel.lantern.pumpkin",
|
||||
(0.0, 0.0, 0.0), (-90.0, 120.0, 0.0), 0.9,
|
||||
),
|
||||
Lantern("PolarisLantern"): VoxTrans(
|
||||
"voxel.lantern.polaris",
|
||||
(0.0, 0.0, 0.0), (-90.0, 120.0, 0.0), 0.9,
|
||||
),
|
||||
// Farming Equipment
|
||||
Tool("common.items.weapons.tool.broom"): VoxTrans(
|
||||
"voxel.weapon.tool.broom-0",
|
||||
@ -2231,6 +2235,10 @@
|
||||
"voxel.armor.misc.head.hood",
|
||||
(0.0, 0.0, 0.0), (-120.0, 210.0,15.0), 1.3,
|
||||
),
|
||||
Armor(Head("DarkHood")): VoxTrans(
|
||||
"voxel.armor.misc.head.hood_dark",
|
||||
(0.0, 0.0, 0.0), (-120.0, 210.0,15.0), 1.3,
|
||||
),
|
||||
Armor(Head("Crown")): VoxTrans(
|
||||
"voxel.armor.misc.head.crown",
|
||||
(0.0, 4.0, 0.0), (-120.0, 210.0,15.0), 1.3,
|
||||
@ -2243,6 +2251,18 @@
|
||||
"voxel.armor.misc.head.spikeguard",
|
||||
(0.0, 0.0, 0.0), (-120.0, 210.0,15.0), 1.3,
|
||||
),
|
||||
Armor(Head("WingedCoronet")): VoxTrans(
|
||||
"voxel.armor.misc.head.winged_coronet",
|
||||
(0.0, 0.0, 0.0), (-120.0, 210.0,15.0), 1.3,
|
||||
),
|
||||
Armor(Head("BorealWarhelm")): VoxTrans(
|
||||
"voxel.armor.misc.head.boreal_warhelm",
|
||||
(0.0, 0.0, 0.0), (-120.0, 210.0,15.0), 1.3,
|
||||
),
|
||||
Armor(Head("WoollyWintercap")): VoxTrans(
|
||||
"voxel.armor.misc.head.woolly_wintercap",
|
||||
(0.0, 0.0, 0.0), (-120.0, 210.0,15.0), 1.3,
|
||||
),
|
||||
// Rings
|
||||
Armor(Ring("Scratched")): VoxTrans(
|
||||
"voxel.armor.misc.ring.scratched",
|
||||
@ -2437,6 +2457,10 @@
|
||||
Consumable("common.items.food.cheese"): Png(
|
||||
"element.items.item_cheese",
|
||||
),
|
||||
Consumable("common.items.food.blue_cheese"): VoxTrans(
|
||||
"voxel.object.blue_cheese",
|
||||
(0.0, 0.0, 0.0), (-60.0, -10.0, 0.0), 0.8,
|
||||
),
|
||||
Consumable("common.items.food.mushroom"): VoxTrans(
|
||||
"voxel.sprite.mushrooms.mushroom-10",
|
||||
(0.0, 0.0, 0.0), (-50.0, 70.0, 40.0), 1.0,
|
||||
|
BIN
assets/voxygen/voxel/armor/misc/head/boreal_warhelm.vox
(Stored with Git LFS)
Executable file
BIN
assets/voxygen/voxel/armor/misc/head/boreal_warhelm.vox
(Stored with Git LFS)
Executable file
Binary file not shown.
BIN
assets/voxygen/voxel/armor/misc/head/hood_dark.vox
(Stored with Git LFS)
Normal file → Executable file
BIN
assets/voxygen/voxel/armor/misc/head/hood_dark.vox
(Stored with Git LFS)
Normal file → Executable file
Binary file not shown.
BIN
assets/voxygen/voxel/armor/misc/head/winged_coronet.vox
(Stored with Git LFS)
Executable file
BIN
assets/voxygen/voxel/armor/misc/head/winged_coronet.vox
(Stored with Git LFS)
Executable file
Binary file not shown.
BIN
assets/voxygen/voxel/armor/misc/head/woolly_wintercap.vox
(Stored with Git LFS)
Executable file
BIN
assets/voxygen/voxel/armor/misc/head/woolly_wintercap.vox
(Stored with Git LFS)
Executable file
Binary file not shown.
@ -274,7 +274,7 @@
|
||||
vox_spec: ("armor.pirate.hat", (-3.0, -5.0, 5.0)),
|
||||
color: None
|
||||
),
|
||||
//
|
||||
// Straw hat
|
||||
(Human, Male, "Straw"): (
|
||||
vox_spec: ("armor.misc.head.straw", (-4.0, -5.0, 5.0)),
|
||||
color: None
|
||||
@ -717,51 +717,51 @@
|
||||
),
|
||||
//
|
||||
(Human, Male, "DarkHood"): (
|
||||
vox_spec: ("armor.misc.head.hood_dark", (-4.0, -4.0, -1.0)),
|
||||
vox_spec: ("armor.misc.head.hood_dark", (-4.0, -6.0, -5.0)),
|
||||
color: None
|
||||
),
|
||||
(Human, Female, "DarkHood"): (
|
||||
vox_spec: ("armor.misc.head.hood_dark", (-4.0, -4.0, -1.0)),
|
||||
vox_spec: ("armor.misc.head.hood_dark", (-4.0, -6.0, -5.0)),
|
||||
color: None
|
||||
),
|
||||
(Elf, Male, "DarkHood"): (
|
||||
vox_spec: ("armor.misc.head.hood_dark", (-3.0, -4.0, -1.0)),
|
||||
vox_spec: ("armor.misc.head.hood_dark", (-3.0, -7.0, -5.0)),
|
||||
color: None
|
||||
),
|
||||
(Elf, Female, "DarkHood"): (
|
||||
vox_spec: ("armor.misc.head.hood_dark", (-3.0, -5.0, -1.0)),
|
||||
vox_spec: ("armor.misc.head.hood_dark", (-3.0, -7.0, -5.0)),
|
||||
color: None
|
||||
),
|
||||
(Dwarf, Male, "DarkHood"): (
|
||||
vox_spec: ("armor.misc.head.hood_dark", (-5.0, -3.0, -1.0)),
|
||||
vox_spec: ("armor.misc.head.hood_dark", (-5.0, -6.0, -5.0)),
|
||||
color: None
|
||||
),
|
||||
(Dwarf, Female, "DarkHood"): (
|
||||
vox_spec: ("armor.misc.head.hood_dark", (-5.0, -3.0, -1.0)),
|
||||
vox_spec: ("armor.misc.head.hood_dark", (-5.0, -6.0, -5.0)),
|
||||
color: None
|
||||
),
|
||||
(Danari, Male, "DarkHood"): (
|
||||
vox_spec: ("armor.misc.head.hood_dark", (-2.0, -4.0, 1.0)),
|
||||
vox_spec: ("armor.misc.head.hood_dark", (-2.0, -7.0, -3.0)),
|
||||
color: None
|
||||
),
|
||||
(Danari, Female, "DarkHood"): (
|
||||
vox_spec: ("armor.misc.head.hood_dark", (-2.0, -4.0, 1.0)),
|
||||
vox_spec: ("armor.misc.head.hood_dark", (-2.0, -7.0, -3.0)),
|
||||
color: None
|
||||
),
|
||||
(Undead, Male, "DarkHood"): (
|
||||
vox_spec: ("armor.misc.head.hood_dark", (-6.0, -4.0, 1.0)),
|
||||
vox_spec: ("armor.misc.head.hood_dark", (-6.0, -7.0, -3.0)),
|
||||
color: None
|
||||
),
|
||||
(Undead, Female, "DarkHood"): (
|
||||
vox_spec: ("armor.misc.head.hood_dark", (-6.0, -4.0, -0.0)),
|
||||
vox_spec: ("armor.misc.head.hood_dark", (-6.0, -7.0, -4.0)),
|
||||
color: None
|
||||
),
|
||||
(Orc, Male, "DarkHood"): (
|
||||
vox_spec: ("armor.misc.head.hood_dark", (-3.0, -2.0, 1.0)),
|
||||
vox_spec: ("armor.misc.head.hood_dark", (-3.0, -5.0, -4.0)),
|
||||
color: None
|
||||
),
|
||||
(Orc, Female, "DarkHood"): (
|
||||
vox_spec: ("armor.misc.head.hood_dark", (-3.0, -5.0, -1.0)),
|
||||
vox_spec: ("armor.misc.head.hood_dark", (-3.0, -8.0, -5.0)),
|
||||
color: None
|
||||
),
|
||||
//
|
||||
@ -911,6 +911,153 @@
|
||||
vox_spec: ("armor.merchant.turban_orc", (-1.0, -4.0, 2.0)),
|
||||
color: None
|
||||
),
|
||||
//Winged Coronet -1, 5
|
||||
(Human, Male, "WingedCoronet"): (
|
||||
vox_spec: ("armor.misc.head.winged_coronet", (-4.0, -4.0, -1.0)),
|
||||
color: None
|
||||
),
|
||||
(Human, Female, "WingedCoronet"): (
|
||||
vox_spec: ("armor.misc.head.winged_coronet", (-4.0, -3.0, -1.0)),
|
||||
color: None
|
||||
),
|
||||
(Elf, Male, "WingedCoronet"): (
|
||||
vox_spec: ("armor.misc.head.winged_coronet", (-3.0, -4.0, -1.0)),
|
||||
color: None
|
||||
),
|
||||
(Elf, Female, "WingedCoronet"): (
|
||||
vox_spec: ("armor.misc.head.winged_coronet", (-3.0, -4.0, -1.0)),
|
||||
color: None
|
||||
),
|
||||
(Dwarf, Male, "WingedCoronet"): (
|
||||
vox_spec: ("armor.misc.head.winged_coronet", (-5.0, -3.0, -2.5)),
|
||||
color: None
|
||||
),
|
||||
(Dwarf, Female, "WingedCoronet"): (
|
||||
vox_spec: ("armor.misc.head.winged_coronet", (-5.0, -3.0, -2.5)),
|
||||
color: None
|
||||
),
|
||||
(Danari, Male, "WingedCoronet"): (
|
||||
vox_spec: ("armor.misc.head.winged_coronet", (-2.0, -3.0, 0.0)),
|
||||
color: None
|
||||
),
|
||||
(Danari, Female, "WingedCoronet"): (
|
||||
vox_spec: ("armor.misc.head.winged_coronet", (-2.0, -3.0, 0.0)),
|
||||
color: None
|
||||
),
|
||||
(Undead, Male, "WingedCoronet"): (
|
||||
vox_spec: ("armor.misc.head.winged_coronet", (-6.0, -5.0, 0.0)),
|
||||
color: None
|
||||
),
|
||||
(Undead, Female, "WingedCoronet"): (
|
||||
vox_spec: ("armor.misc.head.winged_coronet", (-6.0, -5.0, -1.0)),
|
||||
color: None
|
||||
),
|
||||
(Orc, Male, "WingedCoronet"): (
|
||||
vox_spec: ("armor.misc.head.winged_coronet", (-3.0, -2.0, 1.0)),
|
||||
color: None
|
||||
),
|
||||
(Orc, Female, "WingedCoronet"): (
|
||||
vox_spec: ("armor.misc.head.winged_coronet", (-3.0, -5.0, -1.0)),
|
||||
color: None
|
||||
),
|
||||
// Boreal Warhelmet
|
||||
(Human, Male, "BorealWarhelm"): (
|
||||
vox_spec: ("armor.misc.head.boreal_warhelm", (-4.0, -5.0, -4.0)),
|
||||
color: None
|
||||
),
|
||||
(Human, Female, "BorealWarhelm"): (
|
||||
vox_spec: ("armor.misc.head.boreal_warhelm", (-4.0, -5.0, -4.0)),
|
||||
color: None
|
||||
),
|
||||
(Elf, Male, "BorealWarhelm"): (
|
||||
vox_spec: ("armor.misc.head.boreal_warhelm", (-3.0, -6.0, -4.0)),
|
||||
color: None
|
||||
),
|
||||
(Elf, Female, "BorealWarhelm"): (
|
||||
vox_spec: ("armor.misc.head.boreal_warhelm", (-3.0, -6.0, -4.0)),
|
||||
color: None
|
||||
),
|
||||
(Dwarf, Male, "BorealWarhelm"): (
|
||||
vox_spec: ("armor.misc.head.boreal_warhelm", (-5.0, -5.0, -4.0)),
|
||||
color: None
|
||||
),
|
||||
(Dwarf, Female, "BorealWarhelm"): (
|
||||
vox_spec: ("armor.misc.head.boreal_warhelm", (-5.0, -5.0, -4.0)),
|
||||
color: None
|
||||
),
|
||||
(Danari, Male, "BorealWarhelm"): (
|
||||
vox_spec: ("armor.misc.head.boreal_warhelm", (-2.0, -6.0, -2.0)),
|
||||
color: None
|
||||
),
|
||||
(Danari, Female, "BorealWarhelm"): (
|
||||
vox_spec: ("armor.misc.head.boreal_warhelm", (-2.0, -6.0, -2.0)),
|
||||
color: None
|
||||
),
|
||||
(Undead, Male, "BorealWarhelm"): (
|
||||
vox_spec: ("armor.misc.head.boreal_warhelm", (-6.0, -6.0, -3.0)),
|
||||
color: None
|
||||
),
|
||||
(Undead, Female, "BorealWarhelm"): (
|
||||
vox_spec: ("armor.misc.head.boreal_warhelm", (-6.0, -6.0, -3.0)),
|
||||
color: None
|
||||
),
|
||||
(Orc, Male, "BorealWarhelm"): (
|
||||
vox_spec: ("armor.misc.head.boreal_warhelm", (-3.0, -4.0, -2.0)),
|
||||
color: None
|
||||
),
|
||||
(Orc, Female, "BorealWarhelm"): (
|
||||
vox_spec: ("armor.misc.head.boreal_warhelm", (-3.0, -7.0, -4.0)),
|
||||
color: None
|
||||
),
|
||||
// Woolly Wintercap (Christmas hat+event)
|
||||
(Human, Male, "WoollyWintercap"): (
|
||||
vox_spec: ("armor.misc.head.woolly_wintercap", (-4.0, -7.0, -3.0)),
|
||||
color: None
|
||||
),
|
||||
(Human, Female, "WoollyWintercap"): (
|
||||
vox_spec: ("armor.misc.head.woolly_wintercap", (-4.0, -7.0, -3.0)),
|
||||
color: None
|
||||
),
|
||||
(Elf, Male, "WoollyWintercap"): (
|
||||
vox_spec: ("armor.misc.head.woolly_wintercap", (-3.0, -7.0, -4.0)),
|
||||
color: None
|
||||
),
|
||||
(Elf, Female, "WoollyWintercap"): (
|
||||
vox_spec: ("armor.misc.head.woolly_wintercap", (-3.0, -8.0, -4.0)),
|
||||
color: None
|
||||
),
|
||||
(Dwarf, Male, "WoollyWintercap"): (
|
||||
vox_spec: ("armor.misc.head.woolly_wintercap", (-5.0, -6.0, -3.0)),
|
||||
color: None
|
||||
),
|
||||
(Dwarf, Female, "WoollyWintercap"): (
|
||||
vox_spec: ("armor.misc.head.woolly_wintercap", (-5.0, -6.0, -3.0)),
|
||||
color: None
|
||||
),
|
||||
(Danari, Male, "WoollyWintercap"): (
|
||||
vox_spec: ("armor.misc.head.woolly_wintercap", (-2.0, -7.0, -2.0)),
|
||||
color: None
|
||||
),
|
||||
(Danari, Female, "WoollyWintercap"): (
|
||||
vox_spec: ("armor.misc.head.woolly_wintercap", (-2.0, -7.0, -2.0)),
|
||||
color: None
|
||||
),
|
||||
(Undead, Male, "WoollyWintercap"): (
|
||||
vox_spec: ("armor.misc.head.woolly_wintercap", (-6.0, -7.0, -3.0)),
|
||||
color: None
|
||||
),
|
||||
(Undead, Female, "WoollyWintercap"): (
|
||||
vox_spec: ("armor.misc.head.woolly_wintercap", (-6.0, -7.0, -4.0)),
|
||||
color: None
|
||||
),
|
||||
(Orc, Male, "WoollyWintercap"): (
|
||||
vox_spec: ("armor.misc.head.woolly_wintercap", (-3.0, -5.0, -2.0)),
|
||||
color: None
|
||||
),
|
||||
(Orc, Female, "WoollyWintercap"): (
|
||||
vox_spec: ("armor.misc.head.woolly_wintercap", (-3.0, -8.0, -4.0)),
|
||||
color: None
|
||||
),
|
||||
|
||||
}
|
||||
))
|
||||
|
@ -28,5 +28,9 @@
|
||||
vox_spec: ("lantern.pumpkin", (-3.5, -4.0, -8.5)),
|
||||
color: None
|
||||
),
|
||||
"PolarisLantern": (
|
||||
vox_spec: ("lantern.polaris", (-3.0, -4.0, -9.5)),
|
||||
color: None
|
||||
),
|
||||
},
|
||||
))
|
||||
|
BIN
assets/voxygen/voxel/lantern/polaris.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/lantern/polaris.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/voxel/object/blue_cheese.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/object/blue_cheese.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/voxel/sprite/furniture/moravian-star-orange.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/sprite/furniture/moravian-star-orange.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/voxel/sprite/furniture/snowflake_light.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/sprite/furniture/snowflake_light.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/voxel/sprite/furniture/wreath-0.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/sprite/furniture/wreath-0.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -2015,6 +2015,31 @@ WallLampSmall: Some((
|
||||
],
|
||||
wind_sway: 0.0,
|
||||
)),
|
||||
ChristmasOrnament: Some((
|
||||
variations: [
|
||||
(
|
||||
model: "voxygen.voxel.sprite.furniture.snowflake_light",
|
||||
offset: (-5.5, 0.5, 0.0),
|
||||
lod_axes: (1.0, 1.0, 1.0),
|
||||
),
|
||||
(
|
||||
model: "voxygen.voxel.sprite.furniture.moravian-star-orange",
|
||||
offset: (-5.5, -7.5, 0.0),
|
||||
lod_axes: (1.0, 1.0, 1.0),
|
||||
),
|
||||
],
|
||||
wind_sway: 0.2,
|
||||
)),
|
||||
ChristmasWreath: Some((
|
||||
variations: [
|
||||
(
|
||||
model: "voxygen.voxel.sprite.furniture.wreath-0",
|
||||
offset: (-6.5, 0.5, 0.0),
|
||||
lod_axes: (1.0, 1.0, 1.0),
|
||||
),
|
||||
],
|
||||
wind_sway: 0.0,
|
||||
)),
|
||||
// WallSconce
|
||||
WallSconce: Some((
|
||||
variations: [
|
||||
|
@ -0,0 +1,16 @@
|
||||
SpawnEntry (
|
||||
name: "Taiga rare forest wildlife for christmas.",
|
||||
note: "Search for them in the heart of the taiga, if you are feeling lucky.",
|
||||
rules: [
|
||||
Pack(
|
||||
groups: [
|
||||
(9, (1, 1, "common.entity.wild.aggressive.wendigo")),
|
||||
(9, (1, 1, "common.entity.wild.aggressive.dreadhorn")),
|
||||
(1, (1, 1, "common.entity.calendar.christmas.aggressive.yeti")),
|
||||
],
|
||||
is_underwater: false,
|
||||
calendar_events: Some([Christmas]),
|
||||
day_period: [Night, Morning, Noon, Evening],
|
||||
),
|
||||
],
|
||||
)
|
@ -0,0 +1,18 @@
|
||||
SpawnEntry (
|
||||
name: "Tundra rare animals for christmas.",
|
||||
note: "Search for them in the heart of tundra.",
|
||||
rules: [
|
||||
Pack(
|
||||
groups: [
|
||||
(15, (1, 1, "common.entity.wild.aggressive.snow_raptor")),
|
||||
(1, (1, 1, "common.entity.wild.aggressive.wendigo")),
|
||||
(1, (1, 1, "common.entity.wild.aggressive.mammoth")),
|
||||
(1, (1, 1, "common.entity.wild.aggressive.mountain_troll")),
|
||||
(1, (1, 1, "common.entity.calendar.christmas.aggressive.yeti")),
|
||||
],
|
||||
is_underwater: false,
|
||||
calendar_events: Some([Christmas]),
|
||||
day_period: [Night, Morning, Noon, Evening],
|
||||
),
|
||||
],
|
||||
)
|
@ -0,0 +1,18 @@
|
||||
SpawnEntry (
|
||||
name: "Tundra forest animals for christmas.",
|
||||
note: "",
|
||||
rules: [
|
||||
Pack(
|
||||
groups: [
|
||||
(4, (1, 1, "common.entity.wild.aggressive.frostfang")),
|
||||
(4, (1, 1, "common.entity.wild.aggressive.snow_leopard")),
|
||||
(4, (1, 1, "common.entity.wild.aggressive.yale")),
|
||||
(4, (1, 1, "common.entity.wild.aggressive.grolgar")),
|
||||
(1, (1, 1, "common.entity.calendar.christmas.aggressive.yeti")),
|
||||
],
|
||||
is_underwater: false,
|
||||
calendar_events: Some([Christmas]),
|
||||
day_period: [Night, Morning, Noon, Evening],
|
||||
),
|
||||
],
|
||||
)
|
@ -0,0 +1,17 @@
|
||||
SpawnEntry (
|
||||
name: "Tundra animals for christmas.",
|
||||
note: "Usually you can find them in snowy areas.",
|
||||
rules: [
|
||||
Pack(
|
||||
groups: [
|
||||
(6, (1, 3, "common.entity.wild.aggressive.frostfang")),
|
||||
(6, (1, 3, "common.entity.wild.aggressive.snow_raptor")),
|
||||
(6, (1, 3, "common.entity.wild.aggressive.roshwalr")),
|
||||
(1, (1, 1, "common.entity.calendar.christmas.aggressive.yeti")),
|
||||
],
|
||||
is_underwater: false,
|
||||
calendar_events: Some([Christmas]),
|
||||
day_period: [Night, Morning, Noon, Evening],
|
||||
),
|
||||
],
|
||||
)
|
@ -1790,8 +1790,9 @@ impl Client {
|
||||
return Err(Error::Other("Failed to find entity from uid.".to_owned()));
|
||||
}
|
||||
},
|
||||
ServerGeneral::TimeOfDay(time_of_day) => {
|
||||
ServerGeneral::TimeOfDay(time_of_day, calendar) => {
|
||||
self.target_time_of_day = Some(time_of_day);
|
||||
*self.state.ecs_mut().write_resource() = calendar;
|
||||
},
|
||||
ServerGeneral::EntitySync(entity_sync_package) => {
|
||||
self.state
|
||||
|
@ -12,6 +12,7 @@ bin_csv = ["ron", "csv", "structopt"]
|
||||
bin_graphviz = ["petgraph"]
|
||||
bin_cmd_doc_gen = []
|
||||
rrt_pathfinding = ["kiddo"]
|
||||
calendar_events = []
|
||||
|
||||
default = ["simd"]
|
||||
|
||||
@ -26,6 +27,8 @@ serde = { version = "1.0.110", features = ["derive", "rc"] }
|
||||
# Util
|
||||
enum-iterator = "0.7"
|
||||
vek = { version = "=0.14.1", features = ["serde"] }
|
||||
chrono = "0.4"
|
||||
chrono-tz = "0.6"
|
||||
|
||||
# Strum
|
||||
strum = { version = "0.23", features = ["derive"] }
|
||||
|
@ -621,6 +621,11 @@ impl<const AVERAGE_PALETTE: bool> VoxelImageDecoding for TriPngEncoding<AVERAGE_
|
||||
g: 255,
|
||||
b: 255,
|
||||
},
|
||||
Ice => Rgb {
|
||||
r: 150,
|
||||
g: 190,
|
||||
b: 255,
|
||||
},
|
||||
Earth => Rgb {
|
||||
r: 200,
|
||||
g: 140,
|
||||
|
@ -4,6 +4,7 @@ use super::{
|
||||
};
|
||||
use crate::sync;
|
||||
use common::{
|
||||
calendar::Calendar,
|
||||
character::{self, CharacterItem},
|
||||
comp::{self, invite::InviteKind, item::MaterialStatManifest},
|
||||
outcome::Outcome,
|
||||
@ -176,7 +177,7 @@ pub enum ServerGeneral {
|
||||
ChatMsg(comp::ChatMsg),
|
||||
ChatMode(comp::ChatMode),
|
||||
SetPlayerEntity(Uid),
|
||||
TimeOfDay(TimeOfDay),
|
||||
TimeOfDay(TimeOfDay, Calendar),
|
||||
EntitySync(sync::EntitySyncPackage),
|
||||
CompSync(sync::CompSyncPackage<EcsCompPacket>),
|
||||
CreateEntity(sync::EntityPackage<EcsCompPacket>),
|
||||
@ -305,7 +306,7 @@ impl ServerMsg {
|
||||
| ServerGeneral::ChatMsg(_)
|
||||
| ServerGeneral::ChatMode(_)
|
||||
| ServerGeneral::SetPlayerEntity(_)
|
||||
| ServerGeneral::TimeOfDay(_)
|
||||
| ServerGeneral::TimeOfDay(_, _)
|
||||
| ServerGeneral::EntitySync(_)
|
||||
| ServerGeneral::CompSync(_)
|
||||
| ServerGeneral::CreateEntity(_)
|
||||
|
42
common/src/calendar.rs
Normal file
42
common/src/calendar.rs
Normal file
@ -0,0 +1,42 @@
|
||||
use chrono::{DateTime, Datelike, Local, TimeZone, Utc};
|
||||
use chrono_tz::Tz;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
#[repr(u16)]
|
||||
pub enum CalendarEvent {
|
||||
Christmas = 0,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
|
||||
pub struct Calendar {
|
||||
events: Vec<CalendarEvent>,
|
||||
}
|
||||
|
||||
impl Calendar {
|
||||
pub fn is_event(&self, event: CalendarEvent) -> bool { self.events.contains(&event) }
|
||||
|
||||
pub fn events(&self) -> impl ExactSizeIterator<Item = &CalendarEvent> + '_ {
|
||||
self.events.iter()
|
||||
}
|
||||
|
||||
pub fn from_events(events: Vec<CalendarEvent>) -> Self { Self { events } }
|
||||
|
||||
pub fn from_tz(tz: Option<Tz>) -> Self {
|
||||
let mut this = Self::default();
|
||||
|
||||
let now = match tz {
|
||||
Some(tz) => {
|
||||
let utc = Utc::now().naive_utc();
|
||||
DateTime::<Tz>::from_utc(utc, tz.offset_from_utc_datetime(&utc)).naive_local()
|
||||
},
|
||||
None => Local::now().naive_local(),
|
||||
};
|
||||
|
||||
if now.month() == 12 && (20..=30).contains(&now.day()) {
|
||||
this.events.push(CalendarEvent::Christmas);
|
||||
}
|
||||
|
||||
this
|
||||
}
|
||||
}
|
@ -27,6 +27,8 @@ pub use common_assets as assets;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
mod cached_spatial_grid;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub mod calendar;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub mod character;
|
||||
#[cfg(not(target_arch = "wasm32"))] pub mod clock;
|
||||
#[cfg(not(target_arch = "wasm32"))] pub mod cmd;
|
||||
|
@ -305,8 +305,9 @@ pub fn handle_move(data: &JoinData<'_>, update: &mut StateUpdate, efficiency: f3
|
||||
fn basic_move(data: &JoinData<'_>, update: &mut StateUpdate, efficiency: f32) {
|
||||
let efficiency = efficiency * data.stats.move_speed_modifier * data.stats.friction_modifier;
|
||||
|
||||
let accel = if data.physics.on_ground.is_some() {
|
||||
data.body.base_accel()
|
||||
let accel = if let Some(block) = data.physics.on_ground {
|
||||
// FRIC_GROUND temporarily used to normalize things around expected values
|
||||
data.body.base_accel() * block.get_traction() * block.get_friction() / FRIC_GROUND
|
||||
} else {
|
||||
data.body.air_accel()
|
||||
} * efficiency;
|
||||
@ -351,7 +352,10 @@ pub fn handle_forced_movement(
|
||||
match movement {
|
||||
ForcedMovement::Forward { strength } => {
|
||||
let strength = strength * data.stats.move_speed_modifier * data.stats.friction_modifier;
|
||||
if let Some(accel) = data.physics.on_ground.map(|_| data.body.base_accel()) {
|
||||
if let Some(accel) = data.physics.on_ground.map(|block| {
|
||||
// FRIC_GROUND temporarily used to normalize things around expected values
|
||||
data.body.base_accel() * block.get_traction() * block.get_friction() / FRIC_GROUND
|
||||
}) {
|
||||
update.vel.0 += Vec2::broadcast(data.dt.0)
|
||||
* accel
|
||||
* (data.inputs.move_dir + Vec2::from(update.ori))
|
||||
|
@ -1,6 +1,7 @@
|
||||
use super::SpriteKind;
|
||||
use crate::{
|
||||
comp::{fluid_dynamics::LiquidKind, tool::ToolKind},
|
||||
consts::FRIC_GROUND,
|
||||
make_case_elim,
|
||||
};
|
||||
use num_derive::FromPrimitive;
|
||||
@ -49,6 +50,7 @@ make_case_elim!(
|
||||
Wood = 0x40,
|
||||
Leaves = 0x41,
|
||||
GlowingMushroom = 0x42,
|
||||
Ice = 0x43,
|
||||
// 0x43 <= x < 0x50 is reserved for future tree parts
|
||||
// Covers all other cases (we sometimes have bizarrely coloured misc blocks, and also we
|
||||
// often want to experiment with new kinds of block without allocating them a
|
||||
@ -187,6 +189,7 @@ impl Block {
|
||||
| SpriteKind::WallLampSmall
|
||||
| SpriteKind::WallSconce
|
||||
| SpriteKind::FireBowlGround
|
||||
| SpriteKind::ChristmasOrnament
|
||||
| SpriteKind::Orb => Some(16),
|
||||
SpriteKind::Velorite
|
||||
| SpriteKind::VeloriteFrag
|
||||
@ -226,6 +229,7 @@ impl Block {
|
||||
BlockKind::Leaves => (9, 255),
|
||||
BlockKind::Wood => (6, 2),
|
||||
BlockKind::Snow => (6, 2),
|
||||
BlockKind::Ice => (4, 2),
|
||||
_ if self.is_opaque() => (0, 255),
|
||||
_ => (0, 0),
|
||||
}
|
||||
@ -251,6 +255,7 @@ impl Block {
|
||||
BlockKind::Grass => Some(0.5),
|
||||
BlockKind::WeakRock => Some(0.75),
|
||||
BlockKind::Snow => Some(0.1),
|
||||
BlockKind::Ice => Some(0.5),
|
||||
BlockKind::Lava => None,
|
||||
_ => self.get_sprite().and_then(|sprite| match sprite {
|
||||
sprite if sprite.is_container() => None,
|
||||
@ -291,7 +296,9 @@ impl Block {
|
||||
#[inline]
|
||||
pub fn mine_tool(&self) -> Option<ToolKind> {
|
||||
match self.kind() {
|
||||
BlockKind::WeakRock | BlockKind::GlowingWeakRock => Some(ToolKind::Pick),
|
||||
BlockKind::WeakRock | BlockKind::Ice | BlockKind::GlowingWeakRock => {
|
||||
Some(ToolKind::Pick)
|
||||
},
|
||||
_ => self.get_sprite().and_then(|s| s.mine_tool()),
|
||||
}
|
||||
}
|
||||
@ -306,6 +313,29 @@ impl Block {
|
||||
.unwrap_or(1.0)
|
||||
}
|
||||
|
||||
/// Get the friction constant used to calculate surface friction when
|
||||
/// walking/climbing. Currently has no units.
|
||||
#[inline]
|
||||
pub fn get_friction(&self) -> f32 {
|
||||
match self.kind() {
|
||||
BlockKind::Ice => FRIC_GROUND * 0.1,
|
||||
_ => FRIC_GROUND,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the traction permitted by this block as a proportion of the friction
|
||||
/// applied.
|
||||
///
|
||||
/// 1.0 = default, 0.0 = completely inhibits movement, > 1.0 = potential for
|
||||
/// infinite acceleration (in a vacuum).
|
||||
#[inline]
|
||||
pub fn get_traction(&self) -> f32 {
|
||||
match self.kind() {
|
||||
BlockKind::Snow => 0.8,
|
||||
_ => 1.0,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn kind(&self) -> BlockKind { self.kind }
|
||||
|
||||
|
@ -299,6 +299,11 @@ pub struct MapConfig<'a> {
|
||||
///
|
||||
/// Defaults to true.
|
||||
pub is_water: bool,
|
||||
/// When `is_water` is true, controls whether an ice layer should appear on
|
||||
/// that water.
|
||||
///
|
||||
/// Defaults to true.
|
||||
pub is_ice: bool,
|
||||
/// If true, 3D lighting and shading are turned on. Otherwise, a plain
|
||||
/// altitude map is used.
|
||||
///
|
||||
@ -403,6 +408,7 @@ impl<'a> MapConfig<'a> {
|
||||
|
||||
is_basement: false,
|
||||
is_water: true,
|
||||
is_ice: true,
|
||||
is_shaded: true,
|
||||
is_temperature: false,
|
||||
is_humidity: false,
|
||||
@ -581,13 +587,16 @@ impl<'a> MapConfig<'a> {
|
||||
let g_water = 32.0 * water_color_factor;
|
||||
let b_water = 64.0 * water_color_factor;
|
||||
if has_river {
|
||||
let water_rgb = Rgb::new(0, ((g_water) * 1.0) as u8, ((b_water) * 1.0) as u8)
|
||||
.map(|e| e as f64 / 255.0);
|
||||
rgb = water_rgb;
|
||||
k_s = Rgb::new(1.0, 1.0, 1.0);
|
||||
k_d = water_rgb;
|
||||
k_a = water_rgb;
|
||||
alpha = 0.255;
|
||||
// Rudimentary ice check
|
||||
if !rgb.map(|e| e > 0.35).reduce_and() {
|
||||
let water_rgb = Rgb::new(0, ((g_water) * 1.0) as u8, ((b_water) * 1.0) as u8)
|
||||
.map(|e| e as f64 / 255.0);
|
||||
rgb = water_rgb;
|
||||
k_s = Rgb::new(1.0, 1.0, 1.0);
|
||||
k_d = water_rgb;
|
||||
k_a = water_rgb;
|
||||
alpha = 0.255;
|
||||
}
|
||||
}
|
||||
|
||||
let downhill_alt = sample_wpos(downhill_wpos);
|
||||
|
@ -188,6 +188,8 @@ make_case_elim!(
|
||||
JungleLeafyPlant = 0xA1,
|
||||
JungleRedGrass = 0xA2,
|
||||
Bomb = 0xA3,
|
||||
ChristmasOrnament = 0xA4,
|
||||
ChristmasWreath = 0xA5,
|
||||
}
|
||||
);
|
||||
|
||||
@ -451,6 +453,8 @@ impl SpriteKind {
|
||||
| SpriteKind::TanningRack
|
||||
| SpriteKind::Loom
|
||||
| SpriteKind::DismantlingBench
|
||||
| SpriteKind::ChristmasOrnament
|
||||
| SpriteKind::ChristmasWreath
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ use crate::plugin::PluginMgr;
|
||||
#[cfg(feature = "plugins")]
|
||||
use common::uid::UidAllocator;
|
||||
use common::{
|
||||
calendar::Calendar,
|
||||
comp,
|
||||
event::{EventBus, LocalEvent, ServerEvent},
|
||||
outcome::Outcome,
|
||||
@ -199,6 +200,7 @@ impl State {
|
||||
|
||||
// Register synced resources used by the ECS.
|
||||
ecs.insert(TimeOfDay(0.0));
|
||||
ecs.insert(Calendar::default());
|
||||
|
||||
// Register unsynced resources used by the ECS.
|
||||
ecs.insert(Time(0.0));
|
||||
|
@ -1692,8 +1692,18 @@ fn box_voxel_collision<'a, T: BaseVol<Vox = Block> + ReadVol>(
|
||||
physics_state.on_wall = on_wall;
|
||||
let fric_mod = read.stats.get(entity).map_or(1.0, |s| s.friction_modifier);
|
||||
|
||||
if physics_state.on_ground.is_some() || (physics_state.on_wall.is_some() && climbing) {
|
||||
vel.0 *= (1.0 - FRIC_GROUND.min(1.0) * fric_mod).powf(dt.0 * 60.0);
|
||||
let ground_fric = physics_state
|
||||
.on_ground
|
||||
.map(|b| b.get_friction())
|
||||
.unwrap_or(0.0);
|
||||
let wall_fric = if physics_state.on_wall.is_some() && climbing {
|
||||
FRIC_GROUND
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
let fric = ground_fric.max(wall_fric);
|
||||
if fric > 0.0 {
|
||||
vel.0 *= (1.0 - fric.min(1.0) * fric_mod).powf(dt.0 * 60.0);
|
||||
physics_state.ground_vel = ground_vel;
|
||||
}
|
||||
|
||||
|
@ -41,6 +41,7 @@ rustls = { version = "0.20", default-features = false }
|
||||
rustls-pemfile = { version = "0.2.1", default-features = false }
|
||||
atomicwrites = "0.3.0"
|
||||
chrono = { version = "0.4.19", features = ["serde"] }
|
||||
chrono-tz = { version = "0.6", features = ["serde"] }
|
||||
humantime = "2.1.0"
|
||||
itertools = "0.10"
|
||||
lazy_static = "1.4.0"
|
||||
|
@ -2,7 +2,8 @@ use crate::metrics::ChunkGenMetrics;
|
||||
#[cfg(not(feature = "worldgen"))]
|
||||
use crate::test_world::{IndexOwned, World};
|
||||
use common::{
|
||||
generation::ChunkSupplement, resources::TimeOfDay, slowjob::SlowJobPool, terrain::TerrainChunk,
|
||||
calendar::Calendar, generation::ChunkSupplement, resources::TimeOfDay, slowjob::SlowJobPool,
|
||||
terrain::TerrainChunk,
|
||||
};
|
||||
use hashbrown::{hash_map::Entry, HashMap};
|
||||
use specs::Entity as EcsEntity;
|
||||
@ -43,7 +44,7 @@ impl ChunkGenerator {
|
||||
slowjob_pool: &SlowJobPool,
|
||||
world: Arc<World>,
|
||||
index: IndexOwned,
|
||||
time: TimeOfDay,
|
||||
time: (TimeOfDay, Calendar),
|
||||
) {
|
||||
let v = if let Entry::Vacant(v) = self.pending_chunks.entry(key) {
|
||||
v
|
||||
|
@ -123,7 +123,7 @@ impl Client {
|
||||
| ServerGeneral::ChatMsg(_)
|
||||
| ServerGeneral::ChatMode(_)
|
||||
| ServerGeneral::SetPlayerEntity(_)
|
||||
| ServerGeneral::TimeOfDay(_)
|
||||
| ServerGeneral::TimeOfDay(_, _)
|
||||
| ServerGeneral::EntitySync(_)
|
||||
| ServerGeneral::CompSync(_)
|
||||
| ServerGeneral::CreateEntity(_)
|
||||
@ -195,7 +195,7 @@ impl Client {
|
||||
| ServerGeneral::ChatMsg(_)
|
||||
| ServerGeneral::ChatMode(_)
|
||||
| ServerGeneral::SetPlayerEntity(_)
|
||||
| ServerGeneral::TimeOfDay(_)
|
||||
| ServerGeneral::TimeOfDay(_, _)
|
||||
| ServerGeneral::EntitySync(_)
|
||||
| ServerGeneral::CompSync(_)
|
||||
| ServerGeneral::CreateEntity(_)
|
||||
|
@ -18,6 +18,7 @@ use authc::Uuid;
|
||||
use chrono::{NaiveTime, Timelike, Utc};
|
||||
use common::{
|
||||
assets,
|
||||
calendar::Calendar,
|
||||
cmd::{
|
||||
ChatCommand, BUFF_PACK, BUFF_PARSER, ITEM_SPECS, KIT_MANIFEST_PATH, PRESET_MANIFEST_PATH,
|
||||
},
|
||||
@ -986,9 +987,14 @@ fn handle_time(
|
||||
// wait for the next 100th tick to receive the update).
|
||||
let mut tod_lazymsg = None;
|
||||
let clients = server.state.ecs().read_storage::<Client>();
|
||||
let calendar = server.state.ecs().read_resource::<Calendar>();
|
||||
for client in (&clients).join() {
|
||||
let msg = tod_lazymsg
|
||||
.unwrap_or_else(|| client.prepare(ServerGeneral::TimeOfDay(TimeOfDay(new_time))));
|
||||
let msg = tod_lazymsg.unwrap_or_else(|| {
|
||||
client.prepare(ServerGeneral::TimeOfDay(
|
||||
TimeOfDay(new_time),
|
||||
(*calendar).clone(),
|
||||
))
|
||||
});
|
||||
let _ = client.send_prepared(&msg);
|
||||
tod_lazymsg = Some(msg);
|
||||
}
|
||||
@ -2707,6 +2713,7 @@ fn handle_debug_column(
|
||||
_action: &ChatCommand,
|
||||
) -> CmdResult<()> {
|
||||
let sim = server.world.sim();
|
||||
let calendar = (*server.state.ecs().read_resource::<Calendar>()).clone();
|
||||
let sampler = server.world.sample_columns();
|
||||
let wpos = if let (Some(x), Some(y)) = parse_args!(args, i32, i32) {
|
||||
Vec2::new(x, y)
|
||||
@ -2715,7 +2722,7 @@ fn handle_debug_column(
|
||||
// FIXME: Deal with overflow, if needed.
|
||||
pos.0.xy().map(|x| x as i32)
|
||||
};
|
||||
let msg_generator = || {
|
||||
let msg_generator = |calendar| {
|
||||
let alt = sim.get_interpolated(wpos, |chunk| chunk.alt)?;
|
||||
let basement = sim.get_interpolated(wpos, |chunk| chunk.basement)?;
|
||||
let water_alt = sim.get_interpolated(wpos, |chunk| chunk.water_alt)?;
|
||||
@ -2727,7 +2734,7 @@ fn handle_debug_column(
|
||||
let spawn_rate = sim.get_interpolated(wpos, |chunk| chunk.spawn_rate)?;
|
||||
let chunk_pos = wpos.map2(TerrainChunkSize::RECT_SIZE, |e, sz: u32| e / sz as i32);
|
||||
let chunk = sim.get(chunk_pos)?;
|
||||
let col = sampler.get((wpos, server.index.as_index_ref()))?;
|
||||
let col = sampler.get((wpos, server.index.as_index_ref(), Some(calendar)))?;
|
||||
let gradient = sim.get_gradient_approx(chunk_pos)?;
|
||||
let downhill = chunk.downhill;
|
||||
let river = &chunk.river;
|
||||
@ -2766,7 +2773,7 @@ spawn_rate {:?} "#,
|
||||
spawn_rate
|
||||
))
|
||||
};
|
||||
if let Some(s) = msg_generator() {
|
||||
if let Some(s) = msg_generator(&calendar) {
|
||||
server.notify_client(client, ServerGeneral::server_msg(ChatType::CommandInfo, s));
|
||||
Ok(())
|
||||
} else {
|
||||
|
@ -42,7 +42,7 @@ pub use crate::{
|
||||
error::Error,
|
||||
events::Event,
|
||||
input::Input,
|
||||
settings::{EditableSettings, Settings},
|
||||
settings::{CalendarMode, EditableSettings, Settings},
|
||||
};
|
||||
|
||||
#[cfg(feature = "persistent_world")]
|
||||
@ -64,6 +64,7 @@ use crate::{
|
||||
use common::grid::Grid;
|
||||
use common::{
|
||||
assets::AssetExt,
|
||||
calendar::Calendar,
|
||||
character::CharacterId,
|
||||
cmd::ChatCommand,
|
||||
comp::{self, item::MaterialStatManifest},
|
||||
@ -355,6 +356,7 @@ impl Server {
|
||||
// Load default map from assets.
|
||||
FileOpts::LoadAsset(DEFAULT_WORLD_MAP.into())
|
||||
},
|
||||
calendar: Some(settings.calendar_mode.calendar_now()),
|
||||
},
|
||||
state.thread_pool(),
|
||||
);
|
||||
@ -593,6 +595,17 @@ impl Server {
|
||||
self.state.ecs().write_resource::<Tick>().0 += 1;
|
||||
self.state.ecs().write_resource::<TickStart>().0 = Instant::now();
|
||||
|
||||
// Update calendar events as time changes
|
||||
// TODO: If a lot of calendar events get added, this might become expensive.
|
||||
// Maybe don't do this every tick?
|
||||
let new_calendar = self
|
||||
.state
|
||||
.ecs()
|
||||
.read_resource::<Settings>()
|
||||
.calendar_mode
|
||||
.calendar_now();
|
||||
*self.state.ecs_mut().write_resource::<Calendar>() = new_calendar;
|
||||
|
||||
// This tick function is the centre of the Veloren universe. Most server-side
|
||||
// things are managed from here, and as such it's important that it
|
||||
// stays organised. Please consult the core developers before making
|
||||
@ -891,7 +904,10 @@ impl Server {
|
||||
&slow_jobs,
|
||||
Arc::clone(world),
|
||||
index.clone(),
|
||||
*ecs.read_resource::<TimeOfDay>(),
|
||||
(
|
||||
*ecs.read_resource::<TimeOfDay>(),
|
||||
(*ecs.read_resource::<Calendar>()).clone(),
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
@ -1104,7 +1120,10 @@ impl Server {
|
||||
&slow_jobs,
|
||||
Arc::clone(&self.world),
|
||||
self.index.clone(),
|
||||
*ecs.read_resource::<TimeOfDay>(),
|
||||
(
|
||||
*ecs.read_resource::<TimeOfDay>(),
|
||||
(*ecs.read_resource::<Calendar>()).clone(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -14,7 +14,10 @@ pub use server_description::ServerDescription;
|
||||
pub use whitelist::{Whitelist, WhitelistInfo, WhitelistRecord};
|
||||
|
||||
use chrono::Utc;
|
||||
use common::resources::BattleMode;
|
||||
use common::{
|
||||
calendar::{Calendar, CalendarEvent},
|
||||
resources::BattleMode,
|
||||
};
|
||||
use core::time::Duration;
|
||||
use portpicker::pick_unused_port;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@ -62,6 +65,29 @@ impl ServerBattleMode {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub enum CalendarMode {
|
||||
None,
|
||||
Auto,
|
||||
Timezone(chrono_tz::Tz),
|
||||
Events(Vec<CalendarEvent>),
|
||||
}
|
||||
|
||||
impl Default for CalendarMode {
|
||||
fn default() -> Self { Self::Auto }
|
||||
}
|
||||
|
||||
impl CalendarMode {
|
||||
pub fn calendar_now(&self) -> Calendar {
|
||||
match self {
|
||||
CalendarMode::None => Calendar::default(),
|
||||
CalendarMode::Auto => Calendar::from_tz(None),
|
||||
CalendarMode::Timezone(tz) => Calendar::from_tz(Some(*tz)),
|
||||
CalendarMode::Events(events) => Calendar::from_events(events.clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
#[serde(default)]
|
||||
pub struct Settings {
|
||||
@ -84,6 +110,7 @@ pub struct Settings {
|
||||
pub spawn_town: Option<String>,
|
||||
pub safe_spawn: bool,
|
||||
pub max_player_for_kill_broadcast: Option<usize>,
|
||||
pub calendar_mode: CalendarMode,
|
||||
|
||||
/// Experimental feature. No guaranteed forwards-compatibility, may be
|
||||
/// removed at *any time* with no migration.
|
||||
@ -107,6 +134,7 @@ impl Default for Settings {
|
||||
max_view_distance: Some(65),
|
||||
banned_words_files: Vec::new(),
|
||||
max_player_group_size: 6,
|
||||
calendar_mode: CalendarMode::Auto,
|
||||
client_timeout: Duration::from_secs(40),
|
||||
spawn_town: None,
|
||||
safe_spawn: true,
|
||||
|
@ -8,6 +8,7 @@ use crate::{
|
||||
wiring, BattleModeBuffer, SpawnPoint,
|
||||
};
|
||||
use common::{
|
||||
calendar::Calendar,
|
||||
character::CharacterId,
|
||||
combat,
|
||||
combat::DamageContributor,
|
||||
@ -434,7 +435,8 @@ impl StateExt for State {
|
||||
.for_each(|chunk_key| {
|
||||
#[cfg(feature = "worldgen")]
|
||||
{
|
||||
chunk_generator.generate_chunk(None, chunk_key, &slow_jobs, Arc::clone(world), index.clone(), *ecs.read_resource::<TimeOfDay>());
|
||||
let time = (*ecs.read_resource::<TimeOfDay>(), (*ecs.read_resource::<Calendar>()).clone());
|
||||
chunk_generator.generate_chunk(None, chunk_key, &slow_jobs, Arc::clone(world), index.clone(), time);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ use crate::{
|
||||
Tick,
|
||||
};
|
||||
use common::{
|
||||
calendar::Calendar,
|
||||
comp::{Collider, ForceUpdate, Inventory, InventoryUpdate, Last, Ori, Player, Pos, Vel},
|
||||
outcome::Outcome,
|
||||
region::{Event as RegionEvent, RegionMap},
|
||||
@ -28,6 +29,7 @@ impl<'a> System<'a> for Sys {
|
||||
Entities<'a>,
|
||||
Read<'a, Tick>,
|
||||
ReadExpect<'a, TimeOfDay>,
|
||||
ReadExpect<'a, Calendar>,
|
||||
ReadExpect<'a, RegionMap>,
|
||||
ReadStorage<'a, Uid>,
|
||||
ReadStorage<'a, Pos>,
|
||||
@ -61,6 +63,7 @@ impl<'a> System<'a> for Sys {
|
||||
entities,
|
||||
tick,
|
||||
time_of_day,
|
||||
calendar,
|
||||
region_map,
|
||||
uids,
|
||||
positions,
|
||||
@ -360,8 +363,9 @@ impl<'a> System<'a> for Sys {
|
||||
if tick % TOD_SYNC_FREQ == 0 {
|
||||
let mut tod_lazymsg = None;
|
||||
for client in (&clients).join() {
|
||||
let msg = tod_lazymsg
|
||||
.unwrap_or_else(|| client.prepare(ServerGeneral::TimeOfDay(*time_of_day)));
|
||||
let msg = tod_lazymsg.unwrap_or_else(|| {
|
||||
client.prepare(ServerGeneral::TimeOfDay(*time_of_day, (*calendar).clone()))
|
||||
});
|
||||
// We don't care much about stream errors here since they could just represent
|
||||
// network disconnection, which is handled elsewhere.
|
||||
let _ = client.send_prepared(&msg);
|
||||
|
@ -15,6 +15,7 @@ use crate::{
|
||||
ChunkRequest, SpawnPoint, Tick,
|
||||
};
|
||||
use common::{
|
||||
calendar::Calendar,
|
||||
comp::{self, agent, bird_medium, BehaviorCapability, ForceUpdate, Pos, Waypoint},
|
||||
event::{EventBus, ServerEvent},
|
||||
generation::EntityInfo,
|
||||
@ -110,6 +111,7 @@ impl<'a> System<'a> for Sys {
|
||||
Read<'a, SpawnPoint>,
|
||||
Read<'a, Settings>,
|
||||
Read<'a, TimeOfDay>,
|
||||
Read<'a, Calendar>,
|
||||
ReadExpect<'a, SlowJobPool>,
|
||||
ReadExpect<'a, IndexOwned>,
|
||||
ReadExpect<'a, Arc<World>>,
|
||||
@ -142,6 +144,7 @@ impl<'a> System<'a> for Sys {
|
||||
spawn_point,
|
||||
server_settings,
|
||||
time_of_day,
|
||||
calendar,
|
||||
slow_jobs,
|
||||
index,
|
||||
world,
|
||||
@ -176,7 +179,7 @@ impl<'a> System<'a> for Sys {
|
||||
&slow_jobs,
|
||||
Arc::clone(&world),
|
||||
index.clone(),
|
||||
*time_of_day,
|
||||
(*time_of_day, calendar.clone()),
|
||||
)
|
||||
});
|
||||
|
||||
|
@ -23,6 +23,7 @@ pub fn criterion_benchmark(c: &mut Criterion) {
|
||||
// directly with a closure.
|
||||
seed_elements: true,
|
||||
world_file: sim::FileOpts::LoadAsset(sim::DEFAULT_WORLD_MAP.into()),
|
||||
calendar: None,
|
||||
},
|
||||
&pool,
|
||||
);
|
||||
|
@ -206,7 +206,9 @@ impl MovementEventMapper {
|
||||
} else {
|
||||
match underfoot_block_kind {
|
||||
BlockKind::Snow => SfxEvent::Run(BlockKind::Snow),
|
||||
BlockKind::Rock | BlockKind::WeakRock => SfxEvent::Run(BlockKind::Rock),
|
||||
BlockKind::Rock | BlockKind::WeakRock | BlockKind::Ice => {
|
||||
SfxEvent::Run(BlockKind::Rock)
|
||||
},
|
||||
BlockKind::Sand => SfxEvent::Run(BlockKind::Sand),
|
||||
BlockKind::Air => SfxEvent::Idle,
|
||||
_ => SfxEvent::Run(BlockKind::Grass),
|
||||
|
@ -80,7 +80,7 @@ impl BlocksOfInterest {
|
||||
},
|
||||
BlockKind::Snow if rng.gen_range(0..16) == 0 => snow.push(pos),
|
||||
BlockKind::Lava if rng.gen_range(0..5) == 0 => fires.push(pos + Vec3::unit_z()),
|
||||
BlockKind::Snow if rng.gen_range(0..16) == 0 => snow.push(pos),
|
||||
BlockKind::Snow | BlockKind::Ice if rng.gen_range(0..16) == 0 => snow.push(pos),
|
||||
_ => match block.get_sprite() {
|
||||
Some(SpriteKind::Ember) => {
|
||||
fires.push(pos);
|
||||
|
@ -677,6 +677,7 @@ fn main() {
|
||||
WorldOpts {
|
||||
seed_elements: true,
|
||||
world_file: FileOpts::LoadAsset(DEFAULT_WORLD_MAP.into()),
|
||||
calendar: None,
|
||||
},
|
||||
&pool,
|
||||
);
|
||||
|
@ -29,6 +29,7 @@ fn main() -> Result {
|
||||
WorldOpts {
|
||||
seed_elements: true,
|
||||
world_file: FileOpts::LoadAsset(DEFAULT_WORLD_MAP.into()),
|
||||
calendar: None,
|
||||
},
|
||||
&pool,
|
||||
);
|
||||
|
@ -124,6 +124,7 @@ fn main() {
|
||||
WorldOpts {
|
||||
seed_elements: true,
|
||||
world_file: FileOpts::LoadAsset(DEFAULT_WORLD_MAP.into()),
|
||||
calendar: None,
|
||||
},
|
||||
&pool,
|
||||
);
|
||||
|
@ -166,6 +166,7 @@ fn main() {
|
||||
WorldOpts {
|
||||
seed_elements: true,
|
||||
world_file: FileOpts::LoadAsset(DEFAULT_WORLD_MAP.into()),
|
||||
calendar: None,
|
||||
},
|
||||
&pool,
|
||||
);
|
||||
|
@ -35,7 +35,7 @@ fn main() {
|
||||
let pos = focus + Vec2::new(i as i32, j as i32) * scale;
|
||||
|
||||
let (alt, place) = sampler
|
||||
.get((pos, index))
|
||||
.get((pos, index, None))
|
||||
.map(|sample| {
|
||||
(
|
||||
sample.alt.sub(64.0).add(gain).mul(0.7).max(0.0).min(255.0) as u8,
|
||||
|
@ -49,6 +49,7 @@ fn main() {
|
||||
world_file: sim::FileOpts::LoadAsset(veloren_world::sim::DEFAULT_WORLD_MAP.into()),
|
||||
/* world_file: sim::FileOpts::Load(_map_file),
|
||||
* world_file: sim::FileOpts::Save(sim::SizeOpts::default()), */
|
||||
calendar: None,
|
||||
},
|
||||
&threadpool,
|
||||
);
|
||||
@ -66,6 +67,7 @@ fn main() {
|
||||
uniform_idx_as_vec2(map_size_lg, posi)
|
||||
* TerrainChunkSize::RECT_SIZE.map(|e| e as i32),
|
||||
index,
|
||||
None,
|
||||
))
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
@ -177,6 +179,7 @@ fn main() {
|
||||
|
||||
is_basement,
|
||||
is_water,
|
||||
is_ice: true,
|
||||
is_shaded,
|
||||
is_temperature,
|
||||
is_humidity,
|
||||
|
@ -67,6 +67,7 @@ fn generate(db_path: &str, ymin: Option<i32>, ymax: Option<i32>) -> Result<(), B
|
||||
WorldOpts {
|
||||
seed_elements: true,
|
||||
world_file: FileOpts::LoadAsset(DEFAULT_WORLD_MAP.into()),
|
||||
calendar: None,
|
||||
},
|
||||
&pool,
|
||||
);
|
||||
|
@ -1,11 +1,14 @@
|
||||
use crate::{
|
||||
column::{ColumnGen, ColumnSample},
|
||||
util::{FastNoise, RandomField, RandomPerm, Sampler, SmallCache},
|
||||
IndexRef,
|
||||
IndexRef, CONFIG,
|
||||
};
|
||||
use common::terrain::{
|
||||
structure::{self, StructureBlock},
|
||||
Block, BlockKind, SpriteKind,
|
||||
use common::{
|
||||
calendar::{Calendar, CalendarEvent},
|
||||
terrain::{
|
||||
structure::{self, StructureBlock},
|
||||
Block, BlockKind, SpriteKind,
|
||||
},
|
||||
};
|
||||
use core::ops::{Div, Mul, Range};
|
||||
use serde::Deserialize;
|
||||
@ -33,19 +36,25 @@ impl<'a> BlockGen<'a> {
|
||||
cache: &'b mut SmallCache<Option<ColumnSample<'a>>>,
|
||||
wpos: Vec2<i32>,
|
||||
index: IndexRef<'a>,
|
||||
calendar: Option<&'a Calendar>,
|
||||
) -> Option<&'b ColumnSample<'a>> {
|
||||
cache
|
||||
.get(wpos, |wpos| column_gen.get((wpos, index)))
|
||||
.get(wpos, |wpos| column_gen.get((wpos, index, calendar)))
|
||||
.as_ref()
|
||||
}
|
||||
|
||||
pub fn get_z_cache(&mut self, wpos: Vec2<i32>, index: IndexRef<'a>) -> Option<ZCache<'a>> {
|
||||
pub fn get_z_cache(
|
||||
&mut self,
|
||||
wpos: Vec2<i32>,
|
||||
index: IndexRef<'a>,
|
||||
calendar: Option<&'a Calendar>,
|
||||
) -> Option<ZCache<'a>> {
|
||||
let BlockGen { column_gen } = self;
|
||||
|
||||
// Main sample
|
||||
let sample = column_gen.get((wpos, index))?;
|
||||
let sample = column_gen.get((wpos, index, calendar))?;
|
||||
|
||||
Some(ZCache { sample })
|
||||
Some(ZCache { sample, calendar })
|
||||
}
|
||||
|
||||
pub fn get_with_z_cache(&mut self, wpos: Vec3<i32>, z_cache: Option<&ZCache>) -> Option<Block> {
|
||||
@ -65,8 +74,9 @@ impl<'a> BlockGen<'a> {
|
||||
//tree_density,
|
||||
//forest_kind,
|
||||
//close_structures,
|
||||
// marble,
|
||||
// marble_small,
|
||||
marble: _,
|
||||
marble_mid: _,
|
||||
marble_small: _,
|
||||
rock,
|
||||
// temp,
|
||||
// humidity,
|
||||
@ -74,6 +84,8 @@ impl<'a> BlockGen<'a> {
|
||||
snow_cover,
|
||||
cliff_offset,
|
||||
cliff_height,
|
||||
// water_vel,
|
||||
ice_depth,
|
||||
..
|
||||
} = sample;
|
||||
|
||||
@ -195,8 +207,11 @@ impl<'a> BlockGen<'a> {
|
||||
}
|
||||
})
|
||||
.or_else(|| {
|
||||
let over_water = height < water_height;
|
||||
// Water
|
||||
if (wposf.z as f32) < water_height {
|
||||
if over_water && (wposf.z as f32 - water_height).abs() < ice_depth {
|
||||
Some(Block::new(BlockKind::Ice, CONFIG.ice_color))
|
||||
} else if (wposf.z as f32) < water_height {
|
||||
// Ocean
|
||||
Some(water)
|
||||
} else {
|
||||
@ -208,6 +223,7 @@ impl<'a> BlockGen<'a> {
|
||||
|
||||
pub struct ZCache<'a> {
|
||||
pub sample: ColumnSample<'a>,
|
||||
pub calendar: Option<&'a Calendar>,
|
||||
}
|
||||
|
||||
impl<'a> ZCache<'a> {
|
||||
@ -223,7 +239,7 @@ impl<'a> ZCache<'a> {
|
||||
|
||||
let ground_max = self.sample.alt + warp + rocks + 2.0;
|
||||
|
||||
let max = ground_max.max(self.sample.water_level + 2.0);
|
||||
let max = ground_max.max(self.sample.water_level + 2.0 + self.sample.ice_depth);
|
||||
|
||||
(min, max)
|
||||
}
|
||||
@ -237,6 +253,7 @@ pub fn block_from_structure(
|
||||
structure_seed: u32,
|
||||
sample: &ColumnSample,
|
||||
mut with_sprite: impl FnMut(SpriteKind) -> Block,
|
||||
calendar: Option<&Calendar>,
|
||||
) -> Option<Block> {
|
||||
let field = RandomField::new(structure_seed);
|
||||
|
||||
@ -312,15 +329,21 @@ pub fn block_from_structure(
|
||||
};
|
||||
|
||||
range.map(|range| {
|
||||
Block::new(
|
||||
BlockKind::Leaves,
|
||||
Rgb::<f32>::lerp(
|
||||
Rgb::<u8>::from(range.start).map(f32::from),
|
||||
Rgb::<u8>::from(range.end).map(f32::from),
|
||||
lerp,
|
||||
if calendar.map_or(false, |c| c.is_event(CalendarEvent::Christmas))
|
||||
&& field.chance(pos + structure_pos, 0.025)
|
||||
{
|
||||
Block::new(BlockKind::GlowingWeakRock, Rgb::new(255, 0, 0))
|
||||
} else {
|
||||
Block::new(
|
||||
BlockKind::Leaves,
|
||||
Rgb::<f32>::lerp(
|
||||
Rgb::<u8>::from(range.start).map(f32::from),
|
||||
Rgb::<u8>::from(range.end).map(f32::from),
|
||||
lerp,
|
||||
)
|
||||
.map(|e| e as u8),
|
||||
)
|
||||
.map(|e| e as u8),
|
||||
)
|
||||
}
|
||||
})
|
||||
},
|
||||
StructureBlock::BirchWood => {
|
||||
|
@ -8,6 +8,7 @@ use crate::{
|
||||
util::{Grid, Sampler},
|
||||
};
|
||||
use common::{
|
||||
calendar::Calendar,
|
||||
generation::EntityInfo,
|
||||
terrain::{Block, BlockKind, Structure, TerrainChunk, TerrainChunkSize},
|
||||
vol::{ReadVol, RectVolSize, WriteVol},
|
||||
@ -24,9 +25,12 @@ pub struct CanvasInfo<'a> {
|
||||
pub(crate) chunks: &'a WorldSim,
|
||||
pub(crate) index: IndexRef<'a>,
|
||||
pub(crate) chunk: &'a SimChunk,
|
||||
pub(crate) calendar: Option<&'a Calendar>,
|
||||
}
|
||||
|
||||
impl<'a> CanvasInfo<'a> {
|
||||
pub fn calendar(&self) -> Option<&'a Calendar> { self.calendar }
|
||||
|
||||
pub fn wpos(&self) -> Vec2<i32> { self.wpos }
|
||||
|
||||
pub fn area(&self) -> Aabr<i32> {
|
||||
@ -51,9 +55,11 @@ impl<'a> CanvasInfo<'a> {
|
||||
/// This function does not (currently) cache generated columns.
|
||||
pub fn col_or_gen(&self, wpos: Vec2<i32>) -> Option<Cow<'a, ColumnSample>> {
|
||||
self.col(wpos).map(Cow::Borrowed).or_else(|| {
|
||||
Some(Cow::Owned(
|
||||
ColumnGen::new(self.chunks()).get((wpos, self.index()))?,
|
||||
))
|
||||
Some(Cow::Owned(ColumnGen::new(self.chunks()).get((
|
||||
wpos,
|
||||
self.index(),
|
||||
self.calendar,
|
||||
))?))
|
||||
})
|
||||
}
|
||||
|
||||
@ -122,6 +128,7 @@ impl<'a> CanvasInfo<'a> {
|
||||
chunks: sim,
|
||||
index,
|
||||
chunk: &sim_chunk,
|
||||
calendar: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -221,6 +228,7 @@ impl<'a> Canvas<'a> {
|
||||
seed,
|
||||
col,
|
||||
|sprite| block.with_sprite(sprite),
|
||||
info.calendar,
|
||||
) {
|
||||
if !new_block.is_air() {
|
||||
if with_snow && col.snow_cover && above {
|
||||
|
@ -5,6 +5,7 @@ use crate::{
|
||||
IndexRef, CONFIG,
|
||||
};
|
||||
use common::{
|
||||
calendar::{Calendar, CalendarEvent},
|
||||
terrain::{
|
||||
quadratic_nearest_point, river_spline_coeffs, uniform_idx_as_vec2, vec2_as_uniform_idx,
|
||||
TerrainChunkSize,
|
||||
@ -64,10 +65,10 @@ impl<'a> ColumnGen<'a> {
|
||||
}
|
||||
|
||||
impl<'a> Sampler<'a> for ColumnGen<'a> {
|
||||
type Index = (Vec2<i32>, IndexRef<'a>);
|
||||
type Index = (Vec2<i32>, IndexRef<'a>, Option<&'a Calendar>);
|
||||
type Sample = Option<ColumnSample<'a>>;
|
||||
|
||||
fn get(&self, (wpos, index): Self::Index) -> Option<ColumnSample<'a>> {
|
||||
fn get(&self, (wpos, index, calendar): Self::Index) -> Option<ColumnSample<'a>> {
|
||||
let wposf = wpos.map(|e| e as f64);
|
||||
let chunk_pos = wpos.map2(TerrainChunkSize::RECT_SIZE, |e, sz: u32| e / sz as i32);
|
||||
|
||||
@ -90,6 +91,13 @@ impl<'a> Sampler<'a> for ColumnGen<'a> {
|
||||
wpos,
|
||||
|chunk| if chunk.river.near_water() { 1.0 } else { 0.0 },
|
||||
)?;
|
||||
let water_vel = sim.get_interpolated(wpos, |chunk| {
|
||||
if chunk.river.river_kind.is_some() {
|
||||
chunk.river.velocity
|
||||
} else {
|
||||
Vec3::zero()
|
||||
}
|
||||
})?;
|
||||
let alt = sim.get_interpolated_monotone(wpos, |chunk| chunk.alt)?;
|
||||
let surface_veg = sim.get_interpolated_monotone(wpos, |chunk| chunk.surface_veg)?;
|
||||
let sim_chunk = sim.get(chunk_pos)?;
|
||||
@ -1079,24 +1087,29 @@ impl<'a> Sampler<'a> for ColumnGen<'a> {
|
||||
);
|
||||
|
||||
// Snow covering
|
||||
let snow_cover = temp
|
||||
.sub(CONFIG.snow_temp)
|
||||
let thematic_snow = calendar.map_or(false, |c| c.is_event(CalendarEvent::Christmas));
|
||||
let snow_factor = temp
|
||||
.sub(if thematic_snow {
|
||||
CONFIG.tropical_temp
|
||||
} else {
|
||||
CONFIG.snow_temp
|
||||
})
|
||||
.max(-humidity.sub(CONFIG.desert_hum))
|
||||
.mul(4.0)
|
||||
.add(((marble - 0.5) / 0.5) * 0.5)
|
||||
.add(((marble_mid - 0.5) / 0.5) * 0.25)
|
||||
.add(((marble_small - 0.5) / 0.5) * 0.175);
|
||||
let (alt, ground, sub_surface_color) = if snow_cover <= 0.0 && alt > water_level {
|
||||
let (alt, ground, sub_surface_color) = if snow_factor <= 0.0 && alt > water_level {
|
||||
// Allow snow cover.
|
||||
(
|
||||
alt + 1.0 - snow_cover.max(0.0),
|
||||
Rgb::lerp(snow, ground, snow_cover),
|
||||
alt + 1.0 - snow_factor.max(0.0),
|
||||
Rgb::lerp(snow, ground, snow_factor),
|
||||
Lerp::lerp(sub_surface_color, ground, alt.sub(basement).mul(0.15)),
|
||||
)
|
||||
} else {
|
||||
(alt, ground, sub_surface_color)
|
||||
};
|
||||
let snow_cover = snow_cover <= 0.0;
|
||||
let snow_cover = snow_factor <= 0.0;
|
||||
|
||||
// Make river banks not have grass
|
||||
let ground = water_dist
|
||||
@ -1110,6 +1123,29 @@ impl<'a> Sampler<'a> for ColumnGen<'a> {
|
||||
let path = sim.get_nearest_path(wpos);
|
||||
let cave = sim.get_nearest_cave(wpos);
|
||||
|
||||
let ice_depth = if snow_factor < -0.25
|
||||
&& water_vel.magnitude_squared() < (0.1f32 + marble_mid * 0.2).powi(2)
|
||||
{
|
||||
let cliff = (sim.gen_ctx.hill_nz.get((wposf3d.div(180.0)).into_array()) as f32)
|
||||
.add((marble_mid - 0.5) * 0.2)
|
||||
.abs()
|
||||
.powi(3)
|
||||
.mul(32.0);
|
||||
let cliff_ctrl = (sim.gen_ctx.hill_nz.get((wposf3d.div(128.0)).into_array()) as f32)
|
||||
.sub(0.4)
|
||||
.add((marble_mid - 0.5) * 0.2)
|
||||
.mul(32.0)
|
||||
.clamped(0.0, 1.0);
|
||||
|
||||
(((1.0 - Lerp::lerp(marble, Lerp::lerp(marble_mid, marble_small, 0.25), 0.5)) * 5.0
|
||||
- 1.5)
|
||||
.max(0.0)
|
||||
+ cliff * cliff_ctrl)
|
||||
.min((water_level - alt).max(0.0))
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
|
||||
Some(ColumnSample {
|
||||
alt,
|
||||
riverless_alt,
|
||||
@ -1143,6 +1179,7 @@ impl<'a> Sampler<'a> for ColumnGen<'a> {
|
||||
},
|
||||
forest_kind: sim_chunk.forest_kind,
|
||||
marble,
|
||||
marble_mid,
|
||||
marble_small,
|
||||
rock,
|
||||
temp,
|
||||
@ -1156,6 +1193,8 @@ impl<'a> Sampler<'a> for ColumnGen<'a> {
|
||||
snow_cover,
|
||||
cliff_offset,
|
||||
cliff_height,
|
||||
water_vel,
|
||||
ice_depth,
|
||||
|
||||
chunk: sim_chunk,
|
||||
})
|
||||
@ -1175,6 +1214,7 @@ pub struct ColumnSample<'a> {
|
||||
pub tree_density: f32,
|
||||
pub forest_kind: ForestKind,
|
||||
pub marble: f32,
|
||||
pub marble_mid: f32,
|
||||
pub marble_small: f32,
|
||||
pub rock: f32,
|
||||
pub temp: f32,
|
||||
@ -1188,6 +1228,8 @@ pub struct ColumnSample<'a> {
|
||||
pub snow_cover: bool,
|
||||
pub cliff_offset: f32,
|
||||
pub cliff_height: f32,
|
||||
pub water_vel: Vec3<f32>,
|
||||
pub ice_depth: f32,
|
||||
|
||||
pub chunk: &'a SimChunk,
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
use common::assets;
|
||||
use serde::Deserialize;
|
||||
use vek::*;
|
||||
|
||||
pub struct Config {
|
||||
pub sea_level: f32,
|
||||
@ -51,6 +52,8 @@ pub struct Config {
|
||||
/// Rough desired river width-to-depth ratio (in terms of horizontal chunk
|
||||
/// width / m, for some reason). Not exact.
|
||||
pub river_width_to_depth: f32,
|
||||
/// TODO: Move to colors.ron when blockgen can access it
|
||||
pub ice_color: Rgb<u8>,
|
||||
}
|
||||
|
||||
pub const CONFIG: Config = Config {
|
||||
@ -71,6 +74,7 @@ pub const CONFIG: Config = Config {
|
||||
river_max_width: 2.0,
|
||||
river_min_height: 0.25,
|
||||
river_width_to_depth: 8.0,
|
||||
ice_color: Rgb::new(140, 175, 255),
|
||||
};
|
||||
|
||||
#[derive(Deserialize)]
|
||||
|
@ -7,6 +7,7 @@ use crate::{
|
||||
};
|
||||
use common::{
|
||||
assets::AssetHandle,
|
||||
calendar::{Calendar, CalendarEvent},
|
||||
terrain::{
|
||||
structure::{Structure, StructureBlock, StructuresGroup},
|
||||
Block, BlockKind, SpriteKind,
|
||||
@ -33,7 +34,11 @@ static MODEL_RAND: RandomPerm = RandomPerm::new(0xDB21C052);
|
||||
static UNIT_CHOOSER: UnitChooser = UnitChooser::new(0x700F4EC7);
|
||||
static QUIRKY_RAND: RandomPerm = RandomPerm::new(0xA634460F);
|
||||
|
||||
pub fn apply_trees_to(canvas: &mut Canvas, dynamic_rng: &mut impl Rng) {
|
||||
pub fn apply_trees_to(
|
||||
canvas: &mut Canvas,
|
||||
dynamic_rng: &mut impl Rng,
|
||||
calendar: Option<&Calendar>,
|
||||
) {
|
||||
// TODO: Get rid of this
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
enum TreeModel {
|
||||
@ -63,7 +68,7 @@ pub fn apply_trees_to(canvas: &mut Canvas, dynamic_rng: &mut impl Rng) {
|
||||
} in trees
|
||||
{
|
||||
let tree = if let Some(tree) = tree_cache.entry(pos).or_insert_with(|| {
|
||||
let col = ColumnGen::new(info.chunks()).get((pos, info.index()))?;
|
||||
let col = ColumnGen::new(info.chunks()).get((pos, info.index(), calendar))?;
|
||||
|
||||
// Ensure that it's valid to place a *thing* here
|
||||
if col.alt < col.water_level
|
||||
@ -135,7 +140,11 @@ pub fn apply_trees_to(canvas: &mut Canvas, dynamic_rng: &mut impl Rng) {
|
||||
ForestKind::Pine => {
|
||||
break 'model TreeModel::Procedural(
|
||||
ProceduralTree::generate(
|
||||
TreeConfig::pine(&mut RandomPerm::new(seed), scale),
|
||||
TreeConfig::pine(
|
||||
&mut RandomPerm::new(seed),
|
||||
scale,
|
||||
calendar,
|
||||
),
|
||||
&mut RandomPerm::new(seed),
|
||||
),
|
||||
StructureBlock::PineLeaves,
|
||||
@ -256,6 +265,7 @@ pub fn apply_trees_to(canvas: &mut Canvas, dynamic_rng: &mut impl Rng) {
|
||||
tree.seed,
|
||||
col,
|
||||
Block::air,
|
||||
calendar,
|
||||
)
|
||||
.map(|block| {
|
||||
// Add lights to the tree
|
||||
@ -547,7 +557,7 @@ impl TreeConfig {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pine(rng: &mut impl Rng, scale: f32) -> Self {
|
||||
pub fn pine(rng: &mut impl Rng, scale: f32, calendar: Option<&Calendar>) -> Self {
|
||||
let scale = scale * (1.0 + rng.gen::<f32>().powi(4) * 0.5);
|
||||
let log_scale = 1.0 + scale.log2().max(0.0);
|
||||
|
||||
@ -567,7 +577,11 @@ impl TreeConfig {
|
||||
leaf_vertical_scale: 0.3,
|
||||
proportionality: 1.0,
|
||||
inhabited: false,
|
||||
hanging_sprites: &[(0.0001, SpriteKind::Beehive)],
|
||||
hanging_sprites: if calendar.map_or(false, |c| c.is_event(CalendarEvent::Christmas)) {
|
||||
&[(0.0001, SpriteKind::Beehive), (0.01, SpriteKind::Orb)]
|
||||
} else {
|
||||
&[(0.0001, SpriteKind::Beehive)]
|
||||
},
|
||||
trunk_block: StructureBlock::Filled(BlockKind::Wood, Rgb::new(90, 35, 15)),
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
use crate::{column::ColumnSample, sim::SimChunk, IndexRef, CONFIG};
|
||||
use common::{
|
||||
assets::{self, AssetExt},
|
||||
calendar::{Calendar, CalendarEvent},
|
||||
generation::{ChunkSupplement, EntityInfo},
|
||||
resources::TimeOfDay,
|
||||
terrain::Block,
|
||||
@ -40,7 +41,12 @@ impl assets::Asset for SpawnEntry {
|
||||
impl SpawnEntry {
|
||||
pub fn from(asset_specifier: &str) -> Self { Self::load_expect_cloned(asset_specifier) }
|
||||
|
||||
pub fn request(&self, requested_period: DayPeriod, underwater: bool) -> Option<Pack> {
|
||||
pub fn request(
|
||||
&self,
|
||||
requested_period: DayPeriod,
|
||||
calendar: Option<&Calendar>,
|
||||
underwater: bool,
|
||||
) -> Option<Pack> {
|
||||
self.rules
|
||||
.iter()
|
||||
.find(|pack| {
|
||||
@ -48,8 +54,15 @@ impl SpawnEntry {
|
||||
.day_period
|
||||
.iter()
|
||||
.any(|period| *period == requested_period);
|
||||
let calendar_match = if let Some(calendar) = calendar {
|
||||
pack.calendar_events.as_ref().map_or(true, |events| {
|
||||
events.iter().any(|event| calendar.is_event(*event))
|
||||
})
|
||||
} else {
|
||||
false
|
||||
};
|
||||
let water_match = pack.is_underwater == underwater;
|
||||
time_match && water_match
|
||||
time_match && calendar_match && water_match
|
||||
})
|
||||
.cloned()
|
||||
}
|
||||
@ -97,6 +110,9 @@ pub struct Pack {
|
||||
pub groups: Vec<(Weight, (Min, Max, String))>,
|
||||
pub is_underwater: bool,
|
||||
pub day_period: Vec<DayPeriod>,
|
||||
#[serde(default)]
|
||||
pub calendar_events: Option<Vec<CalendarEvent>>, /* None implies that the group isn't
|
||||
* limited by calendar events */
|
||||
}
|
||||
|
||||
impl Pack {
|
||||
@ -128,19 +144,46 @@ pub fn spawn_manifest() -> Vec<(&'static str, DensityFn)> {
|
||||
("world.wildlife.spawn.tundra.core", |c, _col| {
|
||||
close(c.temp, CONFIG.snow_temp, 0.15) * BASE_DENSITY * 0.5
|
||||
}),
|
||||
// Core animals events
|
||||
(
|
||||
"world.wildlife.spawn.calendar.christmas.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
|
||||
}),
|
||||
// Snowy animals event
|
||||
(
|
||||
"world.wildlife.spawn.calendar.christmas.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
|
||||
}),
|
||||
// Forest animals event
|
||||
(
|
||||
"world.wildlife.spawn.calendar.christmas.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| {
|
||||
close(c.temp, CONFIG.snow_temp + 0.2, 0.2) * col.tree_density * BASE_DENSITY * 0.4
|
||||
}),
|
||||
// Forest core animals event
|
||||
(
|
||||
"world.wildlife.spawn.calendar.christmas.taiga.core_forest",
|
||||
|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
|
||||
@ -271,7 +314,7 @@ pub fn apply_wildlife_supplement<'a, R: Rng>(
|
||||
index: IndexRef,
|
||||
chunk: &SimChunk,
|
||||
supplement: &mut ChunkSupplement,
|
||||
time: Option<TimeOfDay>,
|
||||
time: Option<&(TimeOfDay, Calendar)>,
|
||||
) {
|
||||
let scatter = &index.wildlife_spawns;
|
||||
|
||||
@ -289,12 +332,11 @@ pub fn apply_wildlife_supplement<'a, R: Rng>(
|
||||
};
|
||||
|
||||
let underwater = col_sample.water_level > col_sample.alt;
|
||||
let current_day_period;
|
||||
if let Some(time) = time {
|
||||
current_day_period = DayPeriod::from(time.0)
|
||||
let (current_day_period, calendar) = if let Some((time, calendar)) = time {
|
||||
(DayPeriod::from(time.0), Some(calendar))
|
||||
} else {
|
||||
current_day_period = DayPeriod::Noon
|
||||
}
|
||||
(DayPeriod::Noon, None)
|
||||
};
|
||||
|
||||
let entity_group = scatter
|
||||
.iter()
|
||||
@ -305,7 +347,7 @@ pub fn apply_wildlife_supplement<'a, R: Rng>(
|
||||
.then(|| {
|
||||
entry
|
||||
.read()
|
||||
.request(current_day_period, underwater)
|
||||
.request(current_day_period, calendar, underwater)
|
||||
.and_then(|pack| {
|
||||
(dynamic_rng.gen::<f32>() < density * col_sample.spawn_rate
|
||||
&& col_sample.gradient < Some(1.3))
|
||||
|
@ -49,6 +49,7 @@ use crate::{
|
||||
};
|
||||
use common::{
|
||||
assets,
|
||||
calendar::Calendar,
|
||||
generation::{ChunkSupplement, EntityInfo},
|
||||
resources::TimeOfDay,
|
||||
terrain::{
|
||||
@ -178,14 +179,17 @@ impl World {
|
||||
}),
|
||||
)
|
||||
.collect(),
|
||||
..self.sim.get_map(index)
|
||||
..self.sim.get_map(index, self.sim().calendar.as_ref())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn sample_columns(
|
||||
&self,
|
||||
) -> impl Sampler<Index = (Vec2<i32>, IndexRef), Sample = Option<ColumnSample>> + '_ {
|
||||
) -> impl Sampler<
|
||||
Index = (Vec2<i32>, IndexRef, Option<&'_ Calendar>),
|
||||
Sample = Option<ColumnSample>,
|
||||
> + '_ {
|
||||
ColumnGen::new(&self.sim)
|
||||
}
|
||||
|
||||
@ -215,8 +219,10 @@ impl World {
|
||||
chunk_pos: Vec2<i32>,
|
||||
// TODO: misleading name
|
||||
mut should_continue: impl FnMut() -> bool,
|
||||
time: Option<TimeOfDay>,
|
||||
time: Option<(TimeOfDay, Calendar)>,
|
||||
) -> Result<(TerrainChunk, ChunkSupplement), ()> {
|
||||
let calendar = time.as_ref().map(|(_, cal)| cal);
|
||||
|
||||
let mut sampler = self.sample_blocks();
|
||||
|
||||
let chunk_wpos2d = chunk_pos * TerrainChunkSize::RECT_SIZE.map(|e| e as i32);
|
||||
@ -224,7 +230,7 @@ impl World {
|
||||
let grid_border = 4;
|
||||
let zcache_grid = Grid::populate_from(
|
||||
TerrainChunkSize::RECT_SIZE.map(|e| e as i32) + grid_border * 2,
|
||||
|offs| sampler.get_z_cache(chunk_wpos2d - grid_border + offs, index),
|
||||
|offs| sampler.get_z_cache(chunk_wpos2d - grid_border + offs, index, calendar),
|
||||
);
|
||||
|
||||
let air = Block::air(SpriteKind::Empty);
|
||||
@ -347,6 +353,7 @@ impl World {
|
||||
chunks: &self.sim,
|
||||
index,
|
||||
chunk: sim_chunk,
|
||||
calendar,
|
||||
},
|
||||
chunk: &mut chunk,
|
||||
entities: Vec::new(),
|
||||
@ -362,7 +369,7 @@ impl World {
|
||||
layer::apply_shrubs_to(&mut canvas, &mut dynamic_rng);
|
||||
}
|
||||
if index.features.trees {
|
||||
layer::apply_trees_to(&mut canvas, &mut dynamic_rng);
|
||||
layer::apply_trees_to(&mut canvas, &mut dynamic_rng, calendar);
|
||||
}
|
||||
if index.features.scatter {
|
||||
layer::apply_scatter_to(&mut canvas, &mut dynamic_rng);
|
||||
@ -424,7 +431,7 @@ impl World {
|
||||
index,
|
||||
sim_chunk,
|
||||
&mut supplement,
|
||||
time,
|
||||
time.as_ref(),
|
||||
);
|
||||
|
||||
// Apply site supplementary information
|
||||
|
@ -81,6 +81,7 @@ pub fn sample_pos(
|
||||
|
||||
is_basement,
|
||||
is_water,
|
||||
is_ice,
|
||||
is_shaded,
|
||||
is_temperature,
|
||||
is_humidity,
|
||||
@ -133,7 +134,7 @@ pub fn sample_pos(
|
||||
let humidity = humidity.min(1.0).max(0.0);
|
||||
let temperature = temperature.min(1.0).max(-1.0) * 0.5 + 0.5;
|
||||
let wpos = pos * TerrainChunkSize::RECT_SIZE.map(|e| e as i32);
|
||||
let column_rgb_alt = samples
|
||||
let column_data = samples
|
||||
.and_then(|samples| {
|
||||
chunk_idx
|
||||
.and_then(|chunk_idx| samples.get(chunk_idx))
|
||||
@ -162,14 +163,14 @@ pub fn sample_pos(
|
||||
.map(|e| e as f64)
|
||||
};
|
||||
|
||||
(rgb, alt)
|
||||
(rgb, alt, sample.ice_depth)
|
||||
});
|
||||
|
||||
let downhill_wpos = downhill.unwrap_or(wpos + TerrainChunkSize::RECT_SIZE.map(|e| e as i32));
|
||||
let alt = if is_basement {
|
||||
basement
|
||||
} else {
|
||||
column_rgb_alt.map_or(alt, |(_, alt)| alt)
|
||||
column_data.map_or(alt, |(_, alt, _)| alt)
|
||||
};
|
||||
|
||||
let true_water_alt = (alt.max(water_alt) as f64 - focus.z) / gain as f64;
|
||||
@ -189,7 +190,7 @@ pub fn sample_pos(
|
||||
if is_shaded { 1.0 } else { alt },
|
||||
if is_shaded || is_humidity { 1.0 } else { 0.0 },
|
||||
);
|
||||
let column_rgb = column_rgb_alt.map(|(rgb, _)| rgb).unwrap_or(default_rgb);
|
||||
let column_rgb = column_data.map(|(rgb, _, _)| rgb).unwrap_or(default_rgb);
|
||||
let mut connections = [None; 8];
|
||||
let mut has_connections = false;
|
||||
// TODO: Support non-river connections.
|
||||
@ -213,33 +214,38 @@ pub fn sample_pos(
|
||||
});
|
||||
});
|
||||
};
|
||||
let rgb = match (river_kind, (is_water, true_alt >= true_sea_level)) {
|
||||
(_, (false, _)) | (None, (_, true)) | (Some(RiverKind::River { .. }), _) => {
|
||||
let (r, g, b) = (
|
||||
(column_rgb.r
|
||||
* if is_temperature {
|
||||
temperature as f64
|
||||
} else {
|
||||
column_rgb.r
|
||||
})
|
||||
.sqrt(),
|
||||
column_rgb.g,
|
||||
(column_rgb.b
|
||||
* if is_humidity {
|
||||
humidity as f64
|
||||
} else {
|
||||
column_rgb.b
|
||||
})
|
||||
.sqrt(),
|
||||
);
|
||||
Rgb::new((r * 255.0) as u8, (g * 255.0) as u8, (b * 255.0) as u8)
|
||||
},
|
||||
(None | Some(RiverKind::Lake { .. } | RiverKind::Ocean), _) => Rgb::new(
|
||||
0,
|
||||
((g_water - water_depth * g_water) * 1.0) as u8,
|
||||
((b_water - water_depth * b_water) * 1.0) as u8,
|
||||
),
|
||||
};
|
||||
let rgb =
|
||||
if is_water && is_ice && column_data.map_or(false, |(_, _, ice_depth)| ice_depth > 0.0) {
|
||||
CONFIG.ice_color
|
||||
} else {
|
||||
match (river_kind, (is_water, true_alt >= true_sea_level)) {
|
||||
(_, (false, _)) | (None, (_, true)) | (Some(RiverKind::River { .. }), _) => {
|
||||
let (r, g, b) = (
|
||||
(column_rgb.r
|
||||
* if is_temperature {
|
||||
temperature as f64
|
||||
} else {
|
||||
column_rgb.r
|
||||
})
|
||||
.sqrt(),
|
||||
column_rgb.g,
|
||||
(column_rgb.b
|
||||
* if is_humidity {
|
||||
humidity as f64
|
||||
} else {
|
||||
column_rgb.b
|
||||
})
|
||||
.sqrt(),
|
||||
);
|
||||
Rgb::new((r * 255.0) as u8, (g * 255.0) as u8, (b * 255.0) as u8)
|
||||
},
|
||||
(None | Some(RiverKind::Lake { .. } | RiverKind::Ocean), _) => Rgb::new(
|
||||
0,
|
||||
((g_water - water_depth * g_water) * 1.0) as u8,
|
||||
((b_water - water_depth * b_water) * 1.0) as u8,
|
||||
),
|
||||
}
|
||||
};
|
||||
// TODO: Make principled.
|
||||
let rgb = if is_path {
|
||||
Rgb::new(0x37, 0x29, 0x23)
|
||||
|
@ -40,6 +40,7 @@ use crate::{
|
||||
};
|
||||
use common::{
|
||||
assets::{self, AssetExt},
|
||||
calendar::Calendar,
|
||||
grid::Grid,
|
||||
lottery::Lottery,
|
||||
spiral::Spiral2d,
|
||||
@ -183,6 +184,7 @@ pub struct WorldOpts {
|
||||
/// Set to false to disable seeding elements during worldgen.
|
||||
pub seed_elements: bool,
|
||||
pub world_file: FileOpts,
|
||||
pub calendar: Option<Calendar>,
|
||||
}
|
||||
|
||||
impl Default for WorldOpts {
|
||||
@ -190,6 +192,7 @@ impl Default for WorldOpts {
|
||||
Self {
|
||||
seed_elements: true,
|
||||
world_file: Default::default(),
|
||||
calendar: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -385,13 +388,17 @@ pub struct WorldSim {
|
||||
|
||||
pub(crate) gen_ctx: GenCtx,
|
||||
pub rng: ChaChaRng,
|
||||
|
||||
pub(crate) calendar: Option<Calendar>,
|
||||
}
|
||||
|
||||
impl WorldSim {
|
||||
pub fn generate(seed: u32, opts: WorldOpts, threadpool: &rayon::ThreadPool) -> Self {
|
||||
let calendar = opts.calendar; // separate lifetime of elements
|
||||
let world_file = opts.world_file;
|
||||
// Parse out the contents of various map formats into the values we need.
|
||||
let parsed_world_file = (|| {
|
||||
let map = match opts.world_file {
|
||||
let map = match world_file {
|
||||
FileOpts::LoadLegacy(ref path) => {
|
||||
let file = match File::open(path) {
|
||||
Ok(file) => file,
|
||||
@ -491,7 +498,7 @@ impl WorldSim {
|
||||
},
|
||||
})
|
||||
.unwrap_or_else(|| {
|
||||
let size_lg = match opts.world_file {
|
||||
let size_lg = match world_file {
|
||||
FileOpts::Generate(SizeOpts { x_lg, y_lg, .. })
|
||||
| FileOpts::Save(SizeOpts { x_lg, y_lg, .. }) => {
|
||||
MapSizeLg::new(Vec2 { x: x_lg, y: y_lg }).unwrap_or_else(|e| {
|
||||
@ -506,7 +513,7 @@ impl WorldSim {
|
||||
let continent_scale_hack = if let Some(map) = &parsed_world_file {
|
||||
map.continent_scale_hack
|
||||
} else if let FileOpts::Generate(SizeOpts { scale, .. })
|
||||
| FileOpts::Save(SizeOpts { scale, .. }) = opts.world_file
|
||||
| FileOpts::Save(SizeOpts { scale, .. }) = world_file
|
||||
{
|
||||
scale
|
||||
} else {
|
||||
@ -1139,7 +1146,7 @@ impl WorldSim {
|
||||
basement,
|
||||
});
|
||||
(|| {
|
||||
if let FileOpts::Save { .. } = opts.world_file {
|
||||
if let FileOpts::Save { .. } = world_file {
|
||||
use std::time::SystemTime;
|
||||
// Check if folder exists and create it if it does not
|
||||
let mut path = PathBuf::from("./maps");
|
||||
@ -1442,6 +1449,7 @@ impl WorldSim {
|
||||
_locations: Vec::new(),
|
||||
gen_ctx,
|
||||
rng,
|
||||
calendar,
|
||||
};
|
||||
|
||||
this.generate_cliffs();
|
||||
@ -1460,7 +1468,7 @@ impl WorldSim {
|
||||
|
||||
/// Draw a map of the world based on chunk information. Returns a buffer of
|
||||
/// u32s.
|
||||
pub fn get_map(&self, index: IndexRef) -> WorldMapMsg {
|
||||
pub fn get_map(&self, index: IndexRef, calendar: Option<&Calendar>) -> WorldMapMsg {
|
||||
let mut map_config = MapConfig::orthographic(
|
||||
self.map_size_lg(),
|
||||
core::ops::RangeInclusive::new(CONFIG.sea_level, CONFIG.sea_level + self.max_height),
|
||||
@ -1485,8 +1493,11 @@ impl WorldSim {
|
||||
|| Box::new(BlockGen::new(ColumnGen::new(self))),
|
||||
|_block_gen, posi| {
|
||||
let sample = column_sample.get(
|
||||
(uniform_idx_as_vec2(self.map_size_lg(), posi) * TerrainChunkSize::RECT_SIZE.map(|e| e as i32),
|
||||
index)
|
||||
(
|
||||
uniform_idx_as_vec2(self.map_size_lg(), posi) * TerrainChunkSize::RECT_SIZE.map(|e| e as i32),
|
||||
index,
|
||||
calendar,
|
||||
)
|
||||
)?;
|
||||
// sample.water_level = CONFIG.sea_level.max(sample.water_level);
|
||||
|
||||
|
@ -1182,6 +1182,7 @@ mod tests {
|
||||
seed_elements: true,
|
||||
world_file: sim::FileOpts::LoadAsset(sim::DEFAULT_WORLD_MAP.into()),
|
||||
//sim::FileOpts::LoadAsset("world.map.economy_8x8".into()),
|
||||
calendar: None,
|
||||
};
|
||||
let mut index = crate::index::Index::new(seed);
|
||||
info!("Index created");
|
||||
|
@ -7,6 +7,7 @@ use crate::{
|
||||
IndexRef,
|
||||
};
|
||||
use common::{
|
||||
calendar::{Calendar, CalendarEvent},
|
||||
make_case_elim,
|
||||
terrain::{Block, BlockKind, SpriteKind},
|
||||
};
|
||||
@ -107,6 +108,7 @@ pub struct House {
|
||||
pub noise: RandomField,
|
||||
pub roof_ribbing: bool,
|
||||
pub roof_ribbing_diagonal: bool,
|
||||
pub christmas_decorations: bool,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
@ -180,7 +182,7 @@ impl Attr {
|
||||
impl Archetype for House {
|
||||
type Attr = Attr;
|
||||
|
||||
fn generate<R: Rng>(rng: &mut R) -> (Self, Skeleton<Self::Attr>) {
|
||||
fn generate<R: Rng>(rng: &mut R, calendar: Option<&Calendar>) -> (Self, Skeleton<Self::Attr>) {
|
||||
let len = rng.gen_range(-8..24).clamped(0, 20);
|
||||
let locus = 6 + rng.gen_range(0..5);
|
||||
let branches_per_side = 1 + len as usize / 20;
|
||||
@ -239,6 +241,9 @@ impl Archetype for House {
|
||||
noise: RandomField::new(rng.gen()),
|
||||
roof_ribbing: rng.gen(),
|
||||
roof_ribbing_diagonal: rng.gen(),
|
||||
christmas_decorations: calendar
|
||||
.map(|c| c.is_event(CalendarEvent::Christmas))
|
||||
.unwrap_or_default(),
|
||||
};
|
||||
|
||||
(this, skel)
|
||||
@ -261,6 +266,7 @@ impl Archetype for House {
|
||||
let roof_color = *self.colors.roof.elim_case_pure(&colors.roof);
|
||||
let wall_color = *self.colors.wall.elim_case_pure(&colors.wall);
|
||||
let support_color = *self.colors.support.elim_case_pure(&colors.support);
|
||||
let christmas_theme = self.christmas_decorations;
|
||||
|
||||
let profile = Vec2::new(bound_offset.x, z);
|
||||
|
||||
@ -608,12 +614,22 @@ impl Archetype for House {
|
||||
if dist == width + 1
|
||||
&& center_offset.map(|e| e.abs()).reduce_min() == 0
|
||||
&& profile.y == floor_height + 3
|
||||
&& self
|
||||
.noise
|
||||
.chance(Vec3::new(center_offset.x, center_offset.y, z), 0.35)
|
||||
&& self.noise.chance(
|
||||
Vec3::new(center_offset.x, center_offset.y, z),
|
||||
if christmas_theme { 0.70 } else { 0.35 },
|
||||
)
|
||||
&& attr.storey_fill.has_lower()
|
||||
{
|
||||
let ornament =
|
||||
let ornament = if christmas_theme {
|
||||
match self
|
||||
.noise
|
||||
.get(Vec3::new(center_offset.x, center_offset.y, z + 100))
|
||||
% 4
|
||||
{
|
||||
0 => SpriteKind::ChristmasWreath,
|
||||
_ => SpriteKind::ChristmasOrnament,
|
||||
}
|
||||
} else {
|
||||
match self
|
||||
.noise
|
||||
.get(Vec3::new(center_offset.x, center_offset.y, z + 100))
|
||||
@ -624,7 +640,8 @@ impl Archetype for House {
|
||||
4 => SpriteKind::WallSconce,
|
||||
5 => SpriteKind::WallLampSmall,
|
||||
_ => SpriteKind::DungeonWallDecor,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
BlockMask::new(
|
||||
Block::air(ornament).with_ori((edge_ori + 4) % 8).unwrap(),
|
||||
|
@ -5,6 +5,7 @@ use crate::{
|
||||
IndexRef,
|
||||
};
|
||||
use common::{
|
||||
calendar::Calendar,
|
||||
make_case_elim,
|
||||
terrain::{Block, BlockKind, SpriteKind},
|
||||
};
|
||||
@ -56,7 +57,7 @@ make_case_elim!(
|
||||
impl Archetype for Keep {
|
||||
type Attr = Attr;
|
||||
|
||||
fn generate<R: Rng>(rng: &mut R) -> (Self, Skeleton<Self::Attr>) {
|
||||
fn generate<R: Rng>(rng: &mut R, _calendar: Option<&Calendar>) -> (Self, Skeleton<Self::Attr>) {
|
||||
let len = rng.gen_range(-8..24).max(0);
|
||||
let storeys = rng.gen_range(1..3);
|
||||
let skel = Skeleton {
|
||||
|
@ -3,6 +3,7 @@ pub mod keep;
|
||||
|
||||
use super::skeleton::*;
|
||||
use crate::{site::BlockMask, IndexRef};
|
||||
use common::calendar::Calendar;
|
||||
use rand::prelude::*;
|
||||
use serde::Deserialize;
|
||||
use vek::*;
|
||||
@ -16,7 +17,7 @@ pub struct Colors {
|
||||
pub trait Archetype {
|
||||
type Attr;
|
||||
|
||||
fn generate<R: Rng>(rng: &mut R) -> (Self, Skeleton<Self::Attr>)
|
||||
fn generate<R: Rng>(rng: &mut R, calendar: Option<&Calendar>) -> (Self, Skeleton<Self::Attr>)
|
||||
where
|
||||
Self: Sized;
|
||||
|
||||
|
@ -8,7 +8,7 @@ pub use self::{
|
||||
};
|
||||
|
||||
use crate::IndexRef;
|
||||
use common::terrain::Block;
|
||||
use common::{calendar::Calendar, terrain::Block};
|
||||
use rand::prelude::*;
|
||||
use serde::Deserialize;
|
||||
use vek::*;
|
||||
@ -25,11 +25,11 @@ pub struct Building<A: Archetype> {
|
||||
}
|
||||
|
||||
impl<A: Archetype> Building<A> {
|
||||
pub fn generate(rng: &mut impl Rng, origin: Vec3<i32>) -> Self
|
||||
pub fn generate(rng: &mut impl Rng, origin: Vec3<i32>, calendar: Option<&Calendar>) -> Self
|
||||
where
|
||||
A: Sized,
|
||||
{
|
||||
let (archetype, skel) = A::generate(rng);
|
||||
let (archetype, skel) = A::generate(rng, calendar);
|
||||
Self {
|
||||
skel,
|
||||
archetype,
|
||||
|
@ -440,11 +440,13 @@ impl Settlement {
|
||||
StructureKind::Keep(Building::<Keep>::generate(
|
||||
ctx.rng,
|
||||
Vec3::new(house_pos.x, house_pos.y, alt),
|
||||
None,
|
||||
))
|
||||
} else {
|
||||
StructureKind::House(Building::<House>::generate(
|
||||
ctx.rng,
|
||||
Vec3::new(house_pos.x, house_pos.y, alt),
|
||||
ctx.sim.map(|sim| sim.calendar.as_ref()).flatten(),
|
||||
))
|
||||
},
|
||||
};
|
||||
|
@ -26,7 +26,7 @@ impl Tree {
|
||||
alt: land.get_alt_approx(origin) as i32,
|
||||
seed: rng.gen(),
|
||||
tree: {
|
||||
let config = TreeConfig::giant(rng, 4.0, false);
|
||||
let config = TreeConfig::giant(rng, 4.0, true);
|
||||
ProceduralTree::generate(config, rng)
|
||||
},
|
||||
}
|
||||
@ -60,7 +60,7 @@ impl Tree {
|
||||
let wpos = wpos2d.with_z(self.alt + z);
|
||||
let rposf = (wpos - self.origin.with_z(self.alt)).map(|e| e as f32 + 0.5);
|
||||
|
||||
let (branch, leaves, _, _) = self.tree.is_branch_or_leaves_at(rposf);
|
||||
let (branch, leaves, platform, air) = self.tree.is_branch_or_leaves_at(rposf);
|
||||
|
||||
if (branch || leaves) && above && col.snow_cover {
|
||||
canvas.set(
|
||||
@ -69,7 +69,9 @@ impl Tree {
|
||||
);
|
||||
}
|
||||
|
||||
let block = if leaves {
|
||||
let block = if air {
|
||||
Some(Block::empty())
|
||||
} else if leaves {
|
||||
if above && dynamic_rng.gen_bool(0.0005) {
|
||||
canvas.spawn(
|
||||
EntityInfo::at(wpos.map(|e| e as f32) + Vec3::unit_z())
|
||||
@ -96,6 +98,8 @@ impl Tree {
|
||||
Some(Block::new(BlockKind::Leaves, leaf_col.map(|e| e as u8)))
|
||||
} else if branch {
|
||||
Some(Block::new(BlockKind::Wood, Rgb::new(80, 32, 0)))
|
||||
} else if platform {
|
||||
Some(Block::new(BlockKind::Wood, Rgb::new(180, 130, 50)))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
@ -280,6 +280,7 @@ impl Fill {
|
||||
*seed,
|
||||
col_sample,
|
||||
Block::air,
|
||||
canvas_info.calendar(),
|
||||
)
|
||||
}),
|
||||
Fill::Sampling(f) => f(pos),
|
||||
|
Loading…
Reference in New Issue
Block a user