mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Merge branch 'sam/buffs' into 'master'
Auras and buffs no longer need to tick every tick See merge request veloren/veloren!3814
This commit is contained in:
@ -8,10 +8,7 @@ ItemDef(
|
|||||||
kind: Potion,
|
kind: Potion,
|
||||||
data: (
|
data: (
|
||||||
strength: 100.0,
|
strength: 100.0,
|
||||||
duration: Some((
|
duration: Some(1),
|
||||||
secs: 1,
|
|
||||||
nanos: 0,
|
|
||||||
)),
|
|
||||||
),
|
),
|
||||||
cat_ids: [Natural],
|
cat_ids: [Natural],
|
||||||
)),
|
)),
|
||||||
@ -19,8 +16,8 @@ ItemDef(
|
|||||||
kind: PotionSickness,
|
kind: PotionSickness,
|
||||||
data: (
|
data: (
|
||||||
strength: 0.33,
|
strength: 0.33,
|
||||||
duration: Some(( secs: 45, nanos: 0, )),
|
duration: Some(45),
|
||||||
delay: Some(( secs: 1, nanos: 0, ))
|
delay: Some(1)
|
||||||
),
|
),
|
||||||
cat_ids: [Natural],
|
cat_ids: [Natural],
|
||||||
)),
|
)),
|
||||||
|
@ -8,10 +8,7 @@ ItemDef(
|
|||||||
kind: Potion,
|
kind: Potion,
|
||||||
data: (
|
data: (
|
||||||
strength: 100.0,
|
strength: 100.0,
|
||||||
duration: Some((
|
duration: Some(1),
|
||||||
secs: 1,
|
|
||||||
nanos: 0,
|
|
||||||
)),
|
|
||||||
),
|
),
|
||||||
cat_ids: [Natural],
|
cat_ids: [Natural],
|
||||||
)),
|
)),
|
||||||
@ -19,8 +16,7 @@ ItemDef(
|
|||||||
kind: PotionSickness,
|
kind: PotionSickness,
|
||||||
data: (
|
data: (
|
||||||
strength: 0.33,
|
strength: 0.33,
|
||||||
duration: Some(( secs: 45, nanos: 0, )),
|
duration: Some(45),
|
||||||
delay: Some(( secs: 1, nanos: 0, ))
|
|
||||||
),
|
),
|
||||||
cat_ids: [Natural],
|
cat_ids: [Natural],
|
||||||
)),
|
)),
|
||||||
|
@ -8,10 +8,7 @@ ItemDef(
|
|||||||
kind: Potion,
|
kind: Potion,
|
||||||
data: (
|
data: (
|
||||||
strength: 75.0,
|
strength: 75.0,
|
||||||
duration: Some((
|
duration: Some(1),
|
||||||
secs: 1,
|
|
||||||
nanos: 0,
|
|
||||||
)),
|
|
||||||
),
|
),
|
||||||
cat_ids: [Natural],
|
cat_ids: [Natural],
|
||||||
)),
|
)),
|
||||||
@ -19,8 +16,7 @@ ItemDef(
|
|||||||
kind: PotionSickness,
|
kind: PotionSickness,
|
||||||
data: (
|
data: (
|
||||||
strength: 0.33,
|
strength: 0.33,
|
||||||
duration: Some(( secs: 45, nanos: 0, )),
|
duration: Some(45),
|
||||||
delay: Some(( secs: 1, nanos: 0, ))
|
|
||||||
),
|
),
|
||||||
cat_ids: [Natural],
|
cat_ids: [Natural],
|
||||||
)),
|
)),
|
||||||
|
@ -8,10 +8,7 @@ ItemDef(
|
|||||||
kind: Potion,
|
kind: Potion,
|
||||||
data: (
|
data: (
|
||||||
strength: 50.0,
|
strength: 50.0,
|
||||||
duration: Some((
|
duration: Some(1),
|
||||||
secs: 1,
|
|
||||||
nanos: 0,
|
|
||||||
)),
|
|
||||||
),
|
),
|
||||||
cat_ids: [Natural],
|
cat_ids: [Natural],
|
||||||
)),
|
)),
|
||||||
@ -19,8 +16,7 @@ ItemDef(
|
|||||||
kind: PotionSickness,
|
kind: PotionSickness,
|
||||||
data: (
|
data: (
|
||||||
strength: 0.33,
|
strength: 0.33,
|
||||||
duration: Some(( secs: 45, nanos: 0, )),
|
duration: Some(45),
|
||||||
delay: Some(( secs: 1, nanos: 0, ))
|
|
||||||
),
|
),
|
||||||
cat_ids: [Natural],
|
cat_ids: [Natural],
|
||||||
)),
|
)),
|
||||||
|
@ -8,10 +8,7 @@ ItemDef(
|
|||||||
kind: Regeneration,
|
kind: Regeneration,
|
||||||
data: (
|
data: (
|
||||||
strength: 1000,
|
strength: 1000,
|
||||||
duration: Some((
|
duration: Some(999),
|
||||||
secs: 999,
|
|
||||||
nanos: 0,
|
|
||||||
)),
|
|
||||||
),
|
),
|
||||||
cat_ids: [Natural],
|
cat_ids: [Natural],
|
||||||
)),
|
)),
|
||||||
@ -19,10 +16,7 @@ ItemDef(
|
|||||||
kind: EnergyRegen,
|
kind: EnergyRegen,
|
||||||
data: (
|
data: (
|
||||||
strength: 1000,
|
strength: 1000,
|
||||||
duration: Some((
|
duration: Some(999),
|
||||||
secs: 999,
|
|
||||||
nanos: 0,
|
|
||||||
)),
|
|
||||||
),
|
),
|
||||||
cat_ids: [Natural],
|
cat_ids: [Natural],
|
||||||
)),
|
)),
|
||||||
@ -30,10 +24,7 @@ ItemDef(
|
|||||||
kind: IncreaseMaxHealth,
|
kind: IncreaseMaxHealth,
|
||||||
data: (
|
data: (
|
||||||
strength: 50000,
|
strength: 50000,
|
||||||
duration: Some((
|
duration: Some(999),
|
||||||
secs: 999,
|
|
||||||
nanos: 0,
|
|
||||||
)),
|
|
||||||
),
|
),
|
||||||
cat_ids: [Natural],
|
cat_ids: [Natural],
|
||||||
)),
|
)),
|
||||||
@ -41,10 +32,7 @@ ItemDef(
|
|||||||
kind: IncreaseMaxEnergy,
|
kind: IncreaseMaxEnergy,
|
||||||
data: (
|
data: (
|
||||||
strength: 50000,
|
strength: 50000,
|
||||||
duration: Some((
|
duration: Some(999),
|
||||||
secs: 999,
|
|
||||||
nanos: 0,
|
|
||||||
)),
|
|
||||||
),
|
),
|
||||||
cat_ids: [Natural],
|
cat_ids: [Natural],
|
||||||
)),
|
)),
|
||||||
|
@ -8,10 +8,7 @@ ItemDef(
|
|||||||
kind: Saturation,
|
kind: Saturation,
|
||||||
data: (
|
data: (
|
||||||
strength: 2.0,
|
strength: 2.0,
|
||||||
duration: Some((
|
duration: Some(5),
|
||||||
secs: 5,
|
|
||||||
nanos: 0,
|
|
||||||
)),
|
|
||||||
),
|
),
|
||||||
cat_ids: [Natural],
|
cat_ids: [Natural],
|
||||||
)),
|
)),
|
||||||
|
@ -8,10 +8,7 @@ ItemDef(
|
|||||||
kind: Saturation,
|
kind: Saturation,
|
||||||
data: (
|
data: (
|
||||||
strength: 10.0,
|
strength: 10.0,
|
||||||
duration: Some((
|
duration: Some(5),
|
||||||
secs: 5,
|
|
||||||
nanos: 0,
|
|
||||||
)),
|
|
||||||
),
|
),
|
||||||
cat_ids: [Natural],
|
cat_ids: [Natural],
|
||||||
)),
|
)),
|
||||||
@ -19,10 +16,7 @@ ItemDef(
|
|||||||
kind: Regeneration,
|
kind: Regeneration,
|
||||||
data: (
|
data: (
|
||||||
strength: 1.0,
|
strength: 1.0,
|
||||||
duration: Some((
|
duration: Some(70),
|
||||||
secs: 70,
|
|
||||||
nanos: 0,
|
|
||||||
)),
|
|
||||||
),
|
),
|
||||||
cat_ids: [Natural],
|
cat_ids: [Natural],
|
||||||
)),
|
)),
|
||||||
|
@ -8,10 +8,7 @@ ItemDef(
|
|||||||
kind: Saturation,
|
kind: Saturation,
|
||||||
data: (
|
data: (
|
||||||
strength: 5.0,
|
strength: 5.0,
|
||||||
duration: Some((
|
duration: Some(5),
|
||||||
secs: 5,
|
|
||||||
nanos: 0,
|
|
||||||
)),
|
|
||||||
),
|
),
|
||||||
cat_ids: [Natural],
|
cat_ids: [Natural],
|
||||||
)),
|
)),
|
||||||
|
@ -8,10 +8,7 @@ ItemDef(
|
|||||||
kind: Saturation,
|
kind: Saturation,
|
||||||
data: (
|
data: (
|
||||||
strength: 3.0,
|
strength: 3.0,
|
||||||
duration: Some((
|
duration: Some(20),
|
||||||
secs: 20,
|
|
||||||
nanos: 0,
|
|
||||||
)),
|
|
||||||
),
|
),
|
||||||
cat_ids: [Natural],
|
cat_ids: [Natural],
|
||||||
)),
|
)),
|
||||||
|
@ -8,10 +8,7 @@ ItemDef(
|
|||||||
kind: Saturation,
|
kind: Saturation,
|
||||||
data: (
|
data: (
|
||||||
strength: 2.0,
|
strength: 2.0,
|
||||||
duration: Some((
|
duration: Some(5),
|
||||||
secs: 5,
|
|
||||||
nanos: 0,
|
|
||||||
)),
|
|
||||||
),
|
),
|
||||||
cat_ids: [Natural],
|
cat_ids: [Natural],
|
||||||
)),
|
)),
|
||||||
|
@ -8,10 +8,7 @@ ItemDef(
|
|||||||
kind: Saturation,
|
kind: Saturation,
|
||||||
data: (
|
data: (
|
||||||
strength: 1.0,
|
strength: 1.0,
|
||||||
duration: Some((
|
duration: Some(5),
|
||||||
secs: 5,
|
|
||||||
nanos: 0,
|
|
||||||
)),
|
|
||||||
),
|
),
|
||||||
cat_ids: [Natural],
|
cat_ids: [Natural],
|
||||||
)),
|
)),
|
||||||
|
@ -8,10 +8,7 @@ ItemDef(
|
|||||||
kind: Saturation,
|
kind: Saturation,
|
||||||
data: (
|
data: (
|
||||||
strength: 1.5,
|
strength: 1.5,
|
||||||
duration: Some((
|
duration: Some(10),
|
||||||
secs: 10,
|
|
||||||
nanos: 0,
|
|
||||||
)),
|
|
||||||
),
|
),
|
||||||
cat_ids: [Natural],
|
cat_ids: [Natural],
|
||||||
)),
|
)),
|
||||||
|
@ -8,10 +8,7 @@ ItemDef(
|
|||||||
kind: Saturation,
|
kind: Saturation,
|
||||||
data: (
|
data: (
|
||||||
strength: 4.0,
|
strength: 4.0,
|
||||||
duration: Some((
|
duration: Some(5),
|
||||||
secs: 5,
|
|
||||||
nanos: 0,
|
|
||||||
)),
|
|
||||||
),
|
),
|
||||||
cat_ids: [Natural],
|
cat_ids: [Natural],
|
||||||
)),
|
)),
|
||||||
|
@ -8,10 +8,7 @@ ItemDef(
|
|||||||
kind: Saturation,
|
kind: Saturation,
|
||||||
data: (
|
data: (
|
||||||
strength: 1.0,
|
strength: 1.0,
|
||||||
duration: Some((
|
duration: Some(5),
|
||||||
secs: 5,
|
|
||||||
nanos: 0,
|
|
||||||
)),
|
|
||||||
),
|
),
|
||||||
cat_ids: [Natural],
|
cat_ids: [Natural],
|
||||||
)),
|
)),
|
||||||
|
@ -8,10 +8,7 @@ ItemDef(
|
|||||||
kind: Saturation,
|
kind: Saturation,
|
||||||
data: (
|
data: (
|
||||||
strength: 1.0,
|
strength: 1.0,
|
||||||
duration: Some((
|
duration: Some(5),
|
||||||
secs: 5,
|
|
||||||
nanos: 0,
|
|
||||||
)),
|
|
||||||
),
|
),
|
||||||
cat_ids: [Natural],
|
cat_ids: [Natural],
|
||||||
)),
|
)),
|
||||||
|
@ -8,10 +8,7 @@ ItemDef(
|
|||||||
kind: Saturation,
|
kind: Saturation,
|
||||||
data: (
|
data: (
|
||||||
strength: 1.0,
|
strength: 1.0,
|
||||||
duration: Some((
|
duration: Some(5),
|
||||||
secs: 5,
|
|
||||||
nanos: 0,
|
|
||||||
)),
|
|
||||||
),
|
),
|
||||||
cat_ids: [Natural],
|
cat_ids: [Natural],
|
||||||
)),
|
)),
|
||||||
|
@ -8,10 +8,7 @@ ItemDef(
|
|||||||
kind: Saturation,
|
kind: Saturation,
|
||||||
data: (
|
data: (
|
||||||
strength: 4.0,
|
strength: 4.0,
|
||||||
duration: Some((
|
duration: Some(5),
|
||||||
secs: 5,
|
|
||||||
nanos: 0,
|
|
||||||
)),
|
|
||||||
),
|
),
|
||||||
cat_ids: [Natural],
|
cat_ids: [Natural],
|
||||||
)),
|
)),
|
||||||
|
@ -8,10 +8,7 @@ ItemDef(
|
|||||||
kind: Saturation,
|
kind: Saturation,
|
||||||
data: (
|
data: (
|
||||||
strength: 1.0,
|
strength: 1.0,
|
||||||
duration: Some((
|
duration: Some(5),
|
||||||
secs: 5,
|
|
||||||
nanos: 0,
|
|
||||||
)),
|
|
||||||
),
|
),
|
||||||
cat_ids: [Natural],
|
cat_ids: [Natural],
|
||||||
)),
|
)),
|
||||||
|
@ -8,10 +8,7 @@ ItemDef(
|
|||||||
kind: Saturation,
|
kind: Saturation,
|
||||||
data: (
|
data: (
|
||||||
strength: 1.0,
|
strength: 1.0,
|
||||||
duration: Some((
|
duration: Some(5),
|
||||||
secs: 5,
|
|
||||||
nanos: 0,
|
|
||||||
)),
|
|
||||||
),
|
),
|
||||||
cat_ids: [Natural],
|
cat_ids: [Natural],
|
||||||
)),
|
)),
|
||||||
|
@ -8,10 +8,7 @@ ItemDef(
|
|||||||
kind: Saturation,
|
kind: Saturation,
|
||||||
data: (
|
data: (
|
||||||
strength: 2.5,
|
strength: 2.5,
|
||||||
duration: Some((
|
duration: Some(10),
|
||||||
secs: 10,
|
|
||||||
nanos: 0,
|
|
||||||
)),
|
|
||||||
),
|
),
|
||||||
cat_ids: [Natural],
|
cat_ids: [Natural],
|
||||||
)),
|
)),
|
||||||
|
@ -8,10 +8,7 @@ ItemDef(
|
|||||||
kind: Saturation,
|
kind: Saturation,
|
||||||
data: (
|
data: (
|
||||||
strength: .45,
|
strength: .45,
|
||||||
duration: Some((
|
duration: Some(10),
|
||||||
secs: 10,
|
|
||||||
nanos: 0,
|
|
||||||
)),
|
|
||||||
),
|
),
|
||||||
cat_ids: [Natural],
|
cat_ids: [Natural],
|
||||||
)),
|
)),
|
||||||
|
@ -8,10 +8,7 @@ ItemDef(
|
|||||||
kind: Saturation,
|
kind: Saturation,
|
||||||
data: (
|
data: (
|
||||||
strength: 1.5,
|
strength: 1.5,
|
||||||
duration: Some((
|
duration: Some(10),
|
||||||
secs: 10,
|
|
||||||
nanos: 0,
|
|
||||||
)),
|
|
||||||
),
|
),
|
||||||
cat_ids: [Natural],
|
cat_ids: [Natural],
|
||||||
)),
|
)),
|
||||||
|
@ -8,10 +8,7 @@ ItemDef(
|
|||||||
kind: Saturation,
|
kind: Saturation,
|
||||||
data: (
|
data: (
|
||||||
strength: .25,
|
strength: .25,
|
||||||
duration: Some((
|
duration: Some(10),
|
||||||
secs: 10,
|
|
||||||
nanos: 0,
|
|
||||||
)),
|
|
||||||
),
|
),
|
||||||
cat_ids: [Natural],
|
cat_ids: [Natural],
|
||||||
)),
|
)),
|
||||||
|
@ -8,10 +8,7 @@ ItemDef(
|
|||||||
kind: Saturation,
|
kind: Saturation,
|
||||||
data: (
|
data: (
|
||||||
strength: 2.5,
|
strength: 2.5,
|
||||||
duration: Some((
|
duration: Some(10),
|
||||||
secs: 10,
|
|
||||||
nanos: 0,
|
|
||||||
)),
|
|
||||||
),
|
),
|
||||||
cat_ids: [Natural],
|
cat_ids: [Natural],
|
||||||
)),
|
)),
|
||||||
|
@ -8,10 +8,7 @@ ItemDef(
|
|||||||
kind: Saturation,
|
kind: Saturation,
|
||||||
data: (
|
data: (
|
||||||
strength: 5.5,
|
strength: 5.5,
|
||||||
duration: Some((
|
duration: Some(15),
|
||||||
secs: 15,
|
|
||||||
nanos: 0,
|
|
||||||
)),
|
|
||||||
),
|
),
|
||||||
cat_ids: [Natural],
|
cat_ids: [Natural],
|
||||||
)),
|
)),
|
||||||
|
@ -8,10 +8,7 @@ ItemDef(
|
|||||||
kind: Saturation,
|
kind: Saturation,
|
||||||
data: (
|
data: (
|
||||||
strength: .9,
|
strength: .9,
|
||||||
duration: Some((
|
duration: Some(10),
|
||||||
secs: 10,
|
|
||||||
nanos: 0,
|
|
||||||
)),
|
|
||||||
),
|
),
|
||||||
cat_ids: [Natural],
|
cat_ids: [Natural],
|
||||||
)),
|
)),
|
||||||
|
@ -8,10 +8,7 @@ ItemDef(
|
|||||||
kind: Saturation,
|
kind: Saturation,
|
||||||
data: (
|
data: (
|
||||||
strength: .45,
|
strength: .45,
|
||||||
duration: Some((
|
duration: Some(10),
|
||||||
secs: 10,
|
|
||||||
nanos: 0,
|
|
||||||
)),
|
|
||||||
),
|
),
|
||||||
cat_ids: [Natural],
|
cat_ids: [Natural],
|
||||||
)),
|
)),
|
||||||
|
@ -8,10 +8,7 @@ ItemDef(
|
|||||||
kind: Saturation,
|
kind: Saturation,
|
||||||
data: (
|
data: (
|
||||||
strength: 2.5,
|
strength: 2.5,
|
||||||
duration: Some((
|
duration: Some(10),
|
||||||
secs: 10,
|
|
||||||
nanos: 0,
|
|
||||||
)),
|
|
||||||
),
|
),
|
||||||
cat_ids: [Natural],
|
cat_ids: [Natural],
|
||||||
)),
|
)),
|
||||||
|
@ -8,10 +8,7 @@ ItemDef(
|
|||||||
kind: Saturation,
|
kind: Saturation,
|
||||||
data: (
|
data: (
|
||||||
strength: .45,
|
strength: .45,
|
||||||
duration: Some((
|
duration: Some(10),
|
||||||
secs: 10,
|
|
||||||
nanos: 0,
|
|
||||||
)),
|
|
||||||
),
|
),
|
||||||
cat_ids: [Natural],
|
cat_ids: [Natural],
|
||||||
)),
|
)),
|
||||||
|
@ -8,10 +8,7 @@ ItemDef(
|
|||||||
kind: Saturation,
|
kind: Saturation,
|
||||||
data: (
|
data: (
|
||||||
strength: 2.0,
|
strength: 2.0,
|
||||||
duration: Some((
|
duration: Some(10),
|
||||||
secs: 10,
|
|
||||||
nanos: 0,
|
|
||||||
)),
|
|
||||||
),
|
),
|
||||||
cat_ids: [Natural],
|
cat_ids: [Natural],
|
||||||
)),
|
)),
|
||||||
|
@ -8,10 +8,7 @@ ItemDef(
|
|||||||
kind: Saturation,
|
kind: Saturation,
|
||||||
data: (
|
data: (
|
||||||
strength: .36,
|
strength: .36,
|
||||||
duration: Some((
|
duration: Some(10),
|
||||||
secs: 10,
|
|
||||||
nanos: 0,
|
|
||||||
)),
|
|
||||||
),
|
),
|
||||||
cat_ids: [Natural],
|
cat_ids: [Natural],
|
||||||
)),
|
)),
|
||||||
|
@ -8,10 +8,7 @@ ItemDef(
|
|||||||
kind: Saturation,
|
kind: Saturation,
|
||||||
data: (
|
data: (
|
||||||
strength: 1.0,
|
strength: 1.0,
|
||||||
duration: Some((
|
duration: Some(5),
|
||||||
secs: 5,
|
|
||||||
nanos: 0,
|
|
||||||
)),
|
|
||||||
),
|
),
|
||||||
cat_ids: [Natural],
|
cat_ids: [Natural],
|
||||||
)),
|
)),
|
||||||
|
@ -8,10 +8,7 @@ ItemDef(
|
|||||||
kind: Saturation,
|
kind: Saturation,
|
||||||
data: (
|
data: (
|
||||||
strength: 4.0,
|
strength: 4.0,
|
||||||
duration: Some((
|
duration: Some(5),
|
||||||
secs: 5,
|
|
||||||
nanos: 0,
|
|
||||||
)),
|
|
||||||
),
|
),
|
||||||
cat_ids: [Natural],
|
cat_ids: [Natural],
|
||||||
)),
|
)),
|
||||||
|
@ -8,10 +8,7 @@ ItemDef(
|
|||||||
kind: Saturation,
|
kind: Saturation,
|
||||||
data: (
|
data: (
|
||||||
strength: 1.0,
|
strength: 1.0,
|
||||||
duration: Some((
|
duration: Some(5),
|
||||||
secs: 5,
|
|
||||||
nanos: 0,
|
|
||||||
)),
|
|
||||||
),
|
),
|
||||||
cat_ids: [Natural],
|
cat_ids: [Natural],
|
||||||
)),
|
)),
|
||||||
|
@ -8,10 +8,7 @@ ItemDef(
|
|||||||
kind: Saturation,
|
kind: Saturation,
|
||||||
data: (
|
data: (
|
||||||
strength: 2.0,
|
strength: 2.0,
|
||||||
duration: Some((
|
duration: Some(5),
|
||||||
secs: 5,
|
|
||||||
nanos: 0,
|
|
||||||
)),
|
|
||||||
),
|
),
|
||||||
cat_ids: [Natural],
|
cat_ids: [Natural],
|
||||||
)),
|
)),
|
||||||
|
@ -8,10 +8,7 @@ ItemDef(
|
|||||||
kind: Saturation,
|
kind: Saturation,
|
||||||
data: (
|
data: (
|
||||||
strength: 2.0,
|
strength: 2.0,
|
||||||
duration: Some((
|
duration: Some(10),
|
||||||
secs: 10,
|
|
||||||
nanos: 0,
|
|
||||||
)),
|
|
||||||
),
|
),
|
||||||
cat_ids: [Natural],
|
cat_ids: [Natural],
|
||||||
)),
|
)),
|
||||||
|
@ -8,10 +8,7 @@ ItemDef(
|
|||||||
kind: Saturation,
|
kind: Saturation,
|
||||||
data: (
|
data: (
|
||||||
strength: 1.0,
|
strength: 1.0,
|
||||||
duration: Some((
|
duration: Some(5),
|
||||||
secs: 5,
|
|
||||||
nanos: 0,
|
|
||||||
)),
|
|
||||||
),
|
),
|
||||||
cat_ids: [Natural],
|
cat_ids: [Natural],
|
||||||
)),
|
)),
|
||||||
|
@ -8,10 +8,7 @@ ItemDef(
|
|||||||
kind: Frenzied,
|
kind: Frenzied,
|
||||||
data: (
|
data: (
|
||||||
strength: 0.4,
|
strength: 0.4,
|
||||||
duration: Some((
|
duration: Some(60),
|
||||||
secs: 60,
|
|
||||||
nanos: 0,
|
|
||||||
)),
|
|
||||||
),
|
),
|
||||||
cat_ids: [Natural],
|
cat_ids: [Natural],
|
||||||
)),
|
)),
|
||||||
@ -19,10 +16,7 @@ ItemDef(
|
|||||||
kind: Cursed,
|
kind: Cursed,
|
||||||
data: (
|
data: (
|
||||||
strength: 0.35,
|
strength: 0.35,
|
||||||
duration: Some((
|
duration: Some(60),
|
||||||
secs: 60,
|
|
||||||
nanos: 0,
|
|
||||||
)),
|
|
||||||
),
|
),
|
||||||
cat_ids: [Natural],
|
cat_ids: [Natural],
|
||||||
)),
|
)),
|
||||||
|
@ -8,10 +8,7 @@ ItemDef(
|
|||||||
kind: Saturation,
|
kind: Saturation,
|
||||||
data: (
|
data: (
|
||||||
strength: 2.0,
|
strength: 2.0,
|
||||||
duration: Some((
|
duration: Some(10),
|
||||||
secs: 10,
|
|
||||||
nanos: 0,
|
|
||||||
)),
|
|
||||||
),
|
),
|
||||||
cat_ids: [Natural],
|
cat_ids: [Natural],
|
||||||
)),
|
)),
|
||||||
|
@ -8,10 +8,7 @@ ItemDef(
|
|||||||
kind: Saturation,
|
kind: Saturation,
|
||||||
data: (
|
data: (
|
||||||
strength: 1.0,
|
strength: 1.0,
|
||||||
duration: Some((
|
duration: Some(5),
|
||||||
secs: 5,
|
|
||||||
nanos: 0,
|
|
||||||
)),
|
|
||||||
),
|
),
|
||||||
cat_ids: [Natural],
|
cat_ids: [Natural],
|
||||||
)),
|
)),
|
||||||
|
@ -8,10 +8,7 @@ ItemDef(
|
|||||||
kind: Saturation,
|
kind: Saturation,
|
||||||
data: (
|
data: (
|
||||||
strength: 3.0,
|
strength: 3.0,
|
||||||
duration: Some((
|
duration: Some(5),
|
||||||
secs: 5,
|
|
||||||
nanos: 0,
|
|
||||||
)),
|
|
||||||
),
|
),
|
||||||
cat_ids: [Natural],
|
cat_ids: [Natural],
|
||||||
)),
|
)),
|
||||||
|
@ -2135,9 +2135,10 @@ impl Client {
|
|||||||
return Err(Error::Other("Failed to find entity from uid.".into()));
|
return Err(Error::Other("Failed to find entity from uid.".into()));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ServerGeneral::TimeOfDay(time_of_day, calendar) => {
|
ServerGeneral::TimeOfDay(time_of_day, calendar, time) => {
|
||||||
self.target_time_of_day = Some(time_of_day);
|
self.target_time_of_day = Some(time_of_day);
|
||||||
*self.state.ecs_mut().write_resource() = calendar;
|
*self.state.ecs_mut().write_resource() = calendar;
|
||||||
|
*self.state.ecs_mut().write_resource() = time;
|
||||||
},
|
},
|
||||||
ServerGeneral::EntitySync(entity_sync_package) => {
|
ServerGeneral::EntitySync(entity_sync_package) => {
|
||||||
self.state
|
self.state
|
||||||
|
@ -11,7 +11,7 @@ use common::{
|
|||||||
lod,
|
lod,
|
||||||
outcome::Outcome,
|
outcome::Outcome,
|
||||||
recipe::{ComponentRecipeBook, RecipeBook},
|
recipe::{ComponentRecipeBook, RecipeBook},
|
||||||
resources::TimeOfDay,
|
resources::{Time, TimeOfDay},
|
||||||
terrain::{Block, TerrainChunk, TerrainChunkMeta, TerrainChunkSize},
|
terrain::{Block, TerrainChunk, TerrainChunkMeta, TerrainChunkSize},
|
||||||
trade::{PendingTrade, SitePrices, TradeId, TradeResult},
|
trade::{PendingTrade, SitePrices, TradeId, TradeResult},
|
||||||
uid::Uid,
|
uid::Uid,
|
||||||
@ -192,7 +192,7 @@ pub enum ServerGeneral {
|
|||||||
ChatMsg(comp::ChatMsg),
|
ChatMsg(comp::ChatMsg),
|
||||||
ChatMode(comp::ChatMode),
|
ChatMode(comp::ChatMode),
|
||||||
SetPlayerEntity(Uid),
|
SetPlayerEntity(Uid),
|
||||||
TimeOfDay(TimeOfDay, Calendar),
|
TimeOfDay(TimeOfDay, Calendar, Time),
|
||||||
EntitySync(sync::EntitySyncPackage),
|
EntitySync(sync::EntitySyncPackage),
|
||||||
CompSync(sync::CompSyncPackage<EcsCompPacket>, u64),
|
CompSync(sync::CompSyncPackage<EcsCompPacket>, u64),
|
||||||
CreateEntity(sync::EntityPackage<EcsCompPacket>),
|
CreateEntity(sync::EntityPackage<EcsCompPacket>),
|
||||||
@ -340,7 +340,7 @@ impl ServerMsg {
|
|||||||
| ServerGeneral::ChatMsg(_)
|
| ServerGeneral::ChatMsg(_)
|
||||||
| ServerGeneral::ChatMode(_)
|
| ServerGeneral::ChatMode(_)
|
||||||
| ServerGeneral::SetPlayerEntity(_)
|
| ServerGeneral::SetPlayerEntity(_)
|
||||||
| ServerGeneral::TimeOfDay(_, _)
|
| ServerGeneral::TimeOfDay(_, _, _)
|
||||||
| ServerGeneral::EntitySync(_)
|
| ServerGeneral::EntitySync(_)
|
||||||
| ServerGeneral::CompSync(_, _)
|
| ServerGeneral::CompSync(_, _)
|
||||||
| ServerGeneral::CreateEntity(_)
|
| ServerGeneral::CreateEntity(_)
|
||||||
|
@ -13,6 +13,7 @@ use crate::{
|
|||||||
},
|
},
|
||||||
event::ServerEvent,
|
event::ServerEvent,
|
||||||
outcome::Outcome,
|
outcome::Outcome,
|
||||||
|
resources::Secs,
|
||||||
states::utils::StageSection,
|
states::utils::StageSection,
|
||||||
uid::{Uid, UidAllocator},
|
uid::{Uid, UidAllocator},
|
||||||
util::Dir,
|
util::Dir,
|
||||||
@ -27,7 +28,7 @@ use crate::{comp::Group, resources::Time};
|
|||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
use specs::{saveload::MarkerAllocator, Entity as EcsEntity, ReadStorage};
|
use specs::{saveload::MarkerAllocator, Entity as EcsEntity, ReadStorage};
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
use std::{ops::MulAssign, time::Duration};
|
use std::ops::MulAssign;
|
||||||
#[cfg(not(target_arch = "wasm32"))] use vek::*;
|
#[cfg(not(target_arch = "wasm32"))] use vek::*;
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
@ -363,7 +364,9 @@ impl Attack {
|
|||||||
emit(ServerEvent::Buff {
|
emit(ServerEvent::Buff {
|
||||||
entity: target.entity,
|
entity: target.entity,
|
||||||
buff_change: BuffChange::Add(b.to_buff(
|
buff_change: BuffChange::Add(b.to_buff(
|
||||||
|
time,
|
||||||
attacker.map(|a| a.uid),
|
attacker.map(|a| a.uid),
|
||||||
|
target.stats,
|
||||||
applied_damage,
|
applied_damage,
|
||||||
strength_modifier,
|
strength_modifier,
|
||||||
)),
|
)),
|
||||||
@ -529,7 +532,9 @@ impl Attack {
|
|||||||
emit(ServerEvent::Buff {
|
emit(ServerEvent::Buff {
|
||||||
entity: target.entity,
|
entity: target.entity,
|
||||||
buff_change: BuffChange::Add(b.to_buff(
|
buff_change: BuffChange::Add(b.to_buff(
|
||||||
|
time,
|
||||||
attacker.map(|a| a.uid),
|
attacker.map(|a| a.uid),
|
||||||
|
target.stats,
|
||||||
accumulated_damage,
|
accumulated_damage,
|
||||||
strength_modifier,
|
strength_modifier,
|
||||||
)),
|
)),
|
||||||
@ -1027,7 +1032,14 @@ impl MulAssign<f32> for CombatBuffStrength {
|
|||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
impl CombatBuff {
|
impl CombatBuff {
|
||||||
fn to_buff(self, uid: Option<Uid>, damage: f32, strength_modifier: f32) -> Buff {
|
fn to_buff(
|
||||||
|
self,
|
||||||
|
time: Time,
|
||||||
|
uid: Option<Uid>,
|
||||||
|
stats: Option<&Stats>,
|
||||||
|
damage: f32,
|
||||||
|
strength_modifier: f32,
|
||||||
|
) -> Buff {
|
||||||
// TODO: Generate BufCategoryId vec (probably requires damage overhaul?)
|
// TODO: Generate BufCategoryId vec (probably requires damage overhaul?)
|
||||||
let source = if let Some(uid) = uid {
|
let source = if let Some(uid) = uid {
|
||||||
BuffSource::Character { by: uid }
|
BuffSource::Character { by: uid }
|
||||||
@ -1038,11 +1050,13 @@ impl CombatBuff {
|
|||||||
self.kind,
|
self.kind,
|
||||||
BuffData::new(
|
BuffData::new(
|
||||||
self.strength.to_strength(damage, strength_modifier),
|
self.strength.to_strength(damage, strength_modifier),
|
||||||
Some(Duration::from_secs_f32(self.dur_secs)),
|
Some(Secs(self.dur_secs as f64)),
|
||||||
None,
|
None,
|
||||||
),
|
),
|
||||||
Vec::new(),
|
Vec::new(),
|
||||||
source,
|
source,
|
||||||
|
time,
|
||||||
|
stats,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,7 @@ use crate::{
|
|||||||
},
|
},
|
||||||
Body, CharacterState, LightEmitter, StateUpdate,
|
Body, CharacterState, LightEmitter, StateUpdate,
|
||||||
},
|
},
|
||||||
|
resources::Secs,
|
||||||
states::{
|
states::{
|
||||||
behavior::JoinData,
|
behavior::JoinData,
|
||||||
utils::{AbilityInfo, StageSection},
|
utils::{AbilityInfo, StageSection},
|
||||||
@ -696,7 +697,7 @@ pub enum CharacterAbility {
|
|||||||
recover_duration: f32,
|
recover_duration: f32,
|
||||||
targets: combat::GroupTarget,
|
targets: combat::GroupTarget,
|
||||||
auras: Vec<aura::AuraBuffConstructor>,
|
auras: Vec<aura::AuraBuffConstructor>,
|
||||||
aura_duration: f32,
|
aura_duration: Secs,
|
||||||
range: f32,
|
range: f32,
|
||||||
energy_cost: f32,
|
energy_cost: f32,
|
||||||
scales_with_combo: bool,
|
scales_with_combo: bool,
|
||||||
@ -728,7 +729,7 @@ pub enum CharacterAbility {
|
|||||||
recover_duration: f32,
|
recover_duration: f32,
|
||||||
buff_kind: buff::BuffKind,
|
buff_kind: buff::BuffKind,
|
||||||
buff_strength: f32,
|
buff_strength: f32,
|
||||||
buff_duration: Option<f32>,
|
buff_duration: Option<Secs>,
|
||||||
energy_cost: f32,
|
energy_cost: f32,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
meta: AbilityMeta,
|
meta: AbilityMeta,
|
||||||
@ -2051,7 +2052,7 @@ impl CharacterAbility {
|
|||||||
if let Ok(level) = skillset.skill_level(Sceptre(HDuration)) {
|
if let Ok(level) = skillset.skill_level(Sceptre(HDuration)) {
|
||||||
auras.iter_mut().for_each(|ref mut aura| {
|
auras.iter_mut().for_each(|ref mut aura| {
|
||||||
if let Some(ref mut duration) = aura.duration {
|
if let Some(ref mut duration) = aura.duration {
|
||||||
*duration *= modifiers.duration.powi(level.into());
|
*duration *= modifiers.duration.powi(level.into()) as f64;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -2078,7 +2079,7 @@ impl CharacterAbility {
|
|||||||
if let Ok(level) = skillset.skill_level(Sceptre(ADuration)) {
|
if let Ok(level) = skillset.skill_level(Sceptre(ADuration)) {
|
||||||
auras.iter_mut().for_each(|ref mut aura| {
|
auras.iter_mut().for_each(|ref mut aura| {
|
||||||
if let Some(ref mut duration) = aura.duration {
|
if let Some(ref mut duration) = aura.duration {
|
||||||
*duration *= modifiers.duration.powi(level.into());
|
*duration *= modifiers.duration.powi(level.into()) as f64;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -2602,7 +2603,7 @@ impl From<(&CharacterAbility, AbilityInfo, &JoinData<'_>)> for CharacterState {
|
|||||||
recover_duration: Duration::from_secs_f32(*recover_duration),
|
recover_duration: Duration::from_secs_f32(*recover_duration),
|
||||||
targets: *targets,
|
targets: *targets,
|
||||||
auras: auras.clone(),
|
auras: auras.clone(),
|
||||||
aura_duration: Duration::from_secs_f32(*aura_duration),
|
aura_duration: *aura_duration,
|
||||||
range: *range,
|
range: *range,
|
||||||
ability_info,
|
ability_info,
|
||||||
scales_with_combo: *scales_with_combo,
|
scales_with_combo: *scales_with_combo,
|
||||||
@ -2667,7 +2668,7 @@ impl From<(&CharacterAbility, AbilityInfo, &JoinData<'_>)> for CharacterState {
|
|||||||
recover_duration: Duration::from_secs_f32(*recover_duration),
|
recover_duration: Duration::from_secs_f32(*recover_duration),
|
||||||
buff_kind: *buff_kind,
|
buff_kind: *buff_kind,
|
||||||
buff_strength: *buff_strength,
|
buff_strength: *buff_strength,
|
||||||
buff_duration: buff_duration.map(Duration::from_secs_f32),
|
buff_duration: *buff_duration,
|
||||||
ability_info,
|
ability_info,
|
||||||
},
|
},
|
||||||
timer: Duration::default(),
|
timer: Duration::default(),
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
combat::GroupTarget,
|
combat::GroupTarget,
|
||||||
comp::buff::{BuffCategory, BuffData, BuffKind, BuffSource},
|
comp::buff::{BuffCategory, BuffData, BuffKind, BuffSource},
|
||||||
|
resources::{Secs, Time},
|
||||||
uid::Uid,
|
uid::Uid,
|
||||||
};
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use slotmap::{new_key_type, SlotMap};
|
use slotmap::{new_key_type, SlotMap};
|
||||||
use specs::{Component, DerefFlaggedStorage, VecStorage};
|
use specs::{Component, DerefFlaggedStorage, VecStorage};
|
||||||
use std::time::Duration;
|
|
||||||
|
|
||||||
new_key_type! { pub struct AuraKey; }
|
new_key_type! { pub struct AuraKey; }
|
||||||
|
|
||||||
@ -36,8 +36,8 @@ pub struct Aura {
|
|||||||
pub aura_kind: AuraKind,
|
pub aura_kind: AuraKind,
|
||||||
/// The radius of the aura
|
/// The radius of the aura
|
||||||
pub radius: f32,
|
pub radius: f32,
|
||||||
/// How long the aura lasts. None corresponds to an indefinite length
|
// None corresponds to an indefinite aura
|
||||||
pub duration: Option<Duration>,
|
pub end_time: Option<Time>,
|
||||||
/* TODO: Add functionality for fading or a gradient */
|
/* TODO: Add functionality for fading or a gradient */
|
||||||
/// Used to filter which entities this aura will apply to. For example,
|
/// Used to filter which entities this aura will apply to. For example,
|
||||||
/// globally neutral auras which affect all entities will have the type
|
/// globally neutral auras which affect all entities will have the type
|
||||||
@ -92,12 +92,12 @@ impl From<(Option<GroupTarget>, Option<&Uid>)> for AuraTarget {
|
|||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
pub struct AuraData {
|
pub struct AuraData {
|
||||||
pub duration: Option<Duration>,
|
pub duration: Option<Secs>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AuraData {
|
impl AuraData {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
fn new(duration: Option<Duration>) -> Self { Self { duration } }
|
fn new(duration: Option<Secs>) -> Self { Self { duration } }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Aura {
|
impl Aura {
|
||||||
@ -105,13 +105,14 @@ impl Aura {
|
|||||||
pub fn new(
|
pub fn new(
|
||||||
aura_kind: AuraKind,
|
aura_kind: AuraKind,
|
||||||
radius: f32,
|
radius: f32,
|
||||||
duration: Option<Duration>,
|
duration: Option<Secs>,
|
||||||
target: AuraTarget,
|
target: AuraTarget,
|
||||||
|
time: Time,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
aura_kind,
|
aura_kind,
|
||||||
radius,
|
radius,
|
||||||
duration,
|
end_time: duration.map(|dur| Time(time.0 + dur.0)),
|
||||||
target,
|
target,
|
||||||
data: AuraData::new(duration),
|
data: AuraData::new(duration),
|
||||||
}
|
}
|
||||||
@ -142,7 +143,7 @@ impl Auras {
|
|||||||
pub struct AuraBuffConstructor {
|
pub struct AuraBuffConstructor {
|
||||||
pub kind: BuffKind,
|
pub kind: BuffKind,
|
||||||
pub strength: f32,
|
pub strength: f32,
|
||||||
pub duration: Option<f32>,
|
pub duration: Option<Secs>,
|
||||||
pub category: BuffCategory,
|
pub category: BuffCategory,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -151,20 +152,21 @@ impl AuraBuffConstructor {
|
|||||||
self,
|
self,
|
||||||
uid: &Uid,
|
uid: &Uid,
|
||||||
radius: f32,
|
radius: f32,
|
||||||
duration: Option<Duration>,
|
duration: Option<Secs>,
|
||||||
target: AuraTarget,
|
target: AuraTarget,
|
||||||
|
time: Time,
|
||||||
) -> Aura {
|
) -> Aura {
|
||||||
let aura_kind = AuraKind::Buff {
|
let aura_kind = AuraKind::Buff {
|
||||||
kind: self.kind,
|
kind: self.kind,
|
||||||
data: BuffData {
|
data: BuffData {
|
||||||
strength: self.strength,
|
strength: self.strength,
|
||||||
duration: self.duration.map(Duration::from_secs_f32),
|
duration: self.duration,
|
||||||
delay: None,
|
delay: None,
|
||||||
},
|
},
|
||||||
category: self.category,
|
category: self.category,
|
||||||
source: BuffSource::Character { by: *uid },
|
source: BuffSource::Character { by: *uid },
|
||||||
};
|
};
|
||||||
Aura::new(aura_kind, radius, duration, target)
|
Aura::new(aura_kind, radius, duration, target, time)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
#![allow(clippy::nonstandard_macro_braces)] //tmp as of false positive !?
|
#![allow(clippy::nonstandard_macro_braces)] //tmp as of false positive !?
|
||||||
use crate::uid::Uid;
|
use crate::{
|
||||||
use core::{cmp::Ordering, time::Duration};
|
comp::{aura::AuraKey, Stats},
|
||||||
|
resources::{Secs, Time},
|
||||||
|
uid::Uid,
|
||||||
|
};
|
||||||
|
use core::cmp::Ordering;
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
use itertools::Either;
|
use itertools::Either;
|
||||||
@ -143,13 +147,13 @@ impl BuffKind {
|
|||||||
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
|
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
|
||||||
pub struct BuffData {
|
pub struct BuffData {
|
||||||
pub strength: f32,
|
pub strength: f32,
|
||||||
pub duration: Option<Duration>,
|
pub duration: Option<Secs>,
|
||||||
pub delay: Option<Duration>,
|
pub delay: Option<Secs>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
impl BuffData {
|
impl BuffData {
|
||||||
pub fn new(strength: f32, duration: Option<Duration>, delay: Option<Duration>) -> Self {
|
pub fn new(strength: f32, duration: Option<Secs>, delay: Option<Secs>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
strength,
|
strength,
|
||||||
duration,
|
duration,
|
||||||
@ -168,7 +172,7 @@ pub enum BuffCategory {
|
|||||||
Magical,
|
Magical,
|
||||||
Divine,
|
Divine,
|
||||||
PersistOnDeath,
|
PersistOnDeath,
|
||||||
FromAura(bool), // bool used to check if buff recently set by aura
|
FromActiveAura(Uid, AuraKey),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
@ -183,16 +187,11 @@ pub enum BuffEffect {
|
|||||||
/// Periodically damages or heals entity
|
/// Periodically damages or heals entity
|
||||||
HealthChangeOverTime {
|
HealthChangeOverTime {
|
||||||
rate: f32,
|
rate: f32,
|
||||||
accumulated: f32,
|
|
||||||
kind: ModifierKind,
|
kind: ModifierKind,
|
||||||
instance: u64,
|
instance: u64,
|
||||||
},
|
},
|
||||||
/// Periodically consume entity energy
|
/// Periodically consume entity energy
|
||||||
EnergyChangeOverTime {
|
EnergyChangeOverTime { rate: f32, kind: ModifierKind },
|
||||||
rate: f32,
|
|
||||||
accumulated: f32,
|
|
||||||
kind: ModifierKind,
|
|
||||||
},
|
|
||||||
/// Changes maximum health by a certain amount
|
/// Changes maximum health by a certain amount
|
||||||
MaxHealthModifier { value: f32, kind: ModifierKind },
|
MaxHealthModifier { value: f32, kind: ModifierKind },
|
||||||
/// Changes maximum energy by a certain amount
|
/// Changes maximum energy by a certain amount
|
||||||
@ -204,7 +203,6 @@ pub enum BuffEffect {
|
|||||||
rate: f32,
|
rate: f32,
|
||||||
kind: ModifierKind,
|
kind: ModifierKind,
|
||||||
target_fraction: f32,
|
target_fraction: f32,
|
||||||
achieved_fraction: Option<f32>,
|
|
||||||
},
|
},
|
||||||
/// Modifies move speed of target
|
/// Modifies move speed of target
|
||||||
MovementSpeed(f32),
|
MovementSpeed(f32),
|
||||||
@ -215,7 +213,7 @@ pub enum BuffEffect {
|
|||||||
/// Reduces poise damage taken after armor is accounted for by this fraction
|
/// Reduces poise damage taken after armor is accounted for by this fraction
|
||||||
PoiseReduction(f32),
|
PoiseReduction(f32),
|
||||||
/// Reduces amount healed by consumables
|
/// Reduces amount healed by consumables
|
||||||
HealReduction { rate: f32 },
|
HealReduction(f32),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Actual de/buff.
|
/// Actual de/buff.
|
||||||
@ -233,8 +231,8 @@ pub struct Buff {
|
|||||||
pub kind: BuffKind,
|
pub kind: BuffKind,
|
||||||
pub data: BuffData,
|
pub data: BuffData,
|
||||||
pub cat_ids: Vec<BuffCategory>,
|
pub cat_ids: Vec<BuffCategory>,
|
||||||
pub time: Option<Duration>,
|
pub end_time: Option<Time>,
|
||||||
pub delay: Option<Duration>,
|
pub start_time: Time,
|
||||||
pub effects: Vec<BuffEffect>,
|
pub effects: Vec<BuffEffect>,
|
||||||
pub source: BuffSource,
|
pub source: BuffSource,
|
||||||
}
|
}
|
||||||
@ -270,179 +268,129 @@ impl Buff {
|
|||||||
data: BuffData,
|
data: BuffData,
|
||||||
cat_ids: Vec<BuffCategory>,
|
cat_ids: Vec<BuffCategory>,
|
||||||
source: BuffSource,
|
source: BuffSource,
|
||||||
|
time: Time,
|
||||||
|
stats: Option<&Stats>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
// Normalized nonlinear scaling
|
// Normalized nonlinear scaling
|
||||||
let nn_scaling = |a| a / (a + 0.5);
|
let nn_scaling = |a| a / (a + 0.5);
|
||||||
let instance = rand::random();
|
let instance = rand::random();
|
||||||
let (effects, time) = match kind {
|
let effects = match kind {
|
||||||
BuffKind::Bleeding => (
|
BuffKind::Bleeding => vec![BuffEffect::HealthChangeOverTime {
|
||||||
vec![BuffEffect::HealthChangeOverTime {
|
|
||||||
rate: -data.strength,
|
rate: -data.strength,
|
||||||
accumulated: 0.0,
|
|
||||||
kind: ModifierKind::Additive,
|
kind: ModifierKind::Additive,
|
||||||
instance,
|
instance,
|
||||||
}],
|
}],
|
||||||
data.duration,
|
BuffKind::Regeneration | BuffKind::Saturation => {
|
||||||
),
|
|
||||||
BuffKind::Regeneration | BuffKind::Saturation | BuffKind::Potion => (
|
|
||||||
vec![BuffEffect::HealthChangeOverTime {
|
vec![BuffEffect::HealthChangeOverTime {
|
||||||
rate: data.strength,
|
rate: data.strength,
|
||||||
accumulated: 0.0,
|
|
||||||
kind: ModifierKind::Additive,
|
kind: ModifierKind::Additive,
|
||||||
instance,
|
instance,
|
||||||
}],
|
}]
|
||||||
data.duration,
|
},
|
||||||
),
|
BuffKind::Potion => {
|
||||||
BuffKind::CampfireHeal => (
|
|
||||||
vec![BuffEffect::HealthChangeOverTime {
|
vec![BuffEffect::HealthChangeOverTime {
|
||||||
|
rate: data.strength * stats.map_or(1.0, |s| s.heal_multiplier),
|
||||||
|
kind: ModifierKind::Additive,
|
||||||
|
instance,
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
BuffKind::CampfireHeal => vec![BuffEffect::HealthChangeOverTime {
|
||||||
rate: data.strength,
|
rate: data.strength,
|
||||||
accumulated: 0.0,
|
|
||||||
kind: ModifierKind::Fractional,
|
kind: ModifierKind::Fractional,
|
||||||
instance,
|
instance,
|
||||||
}],
|
}],
|
||||||
data.duration,
|
BuffKind::Cursed => vec![
|
||||||
),
|
|
||||||
BuffKind::Cursed => (
|
|
||||||
vec![
|
|
||||||
BuffEffect::MaxHealthChangeOverTime {
|
BuffEffect::MaxHealthChangeOverTime {
|
||||||
rate: -1.0,
|
rate: -1.0,
|
||||||
kind: ModifierKind::Additive,
|
kind: ModifierKind::Additive,
|
||||||
target_fraction: 1.0 - data.strength,
|
target_fraction: 1.0 - data.strength,
|
||||||
achieved_fraction: None,
|
|
||||||
},
|
},
|
||||||
BuffEffect::HealthChangeOverTime {
|
BuffEffect::HealthChangeOverTime {
|
||||||
rate: -1.0,
|
rate: -1.0,
|
||||||
accumulated: 0.0,
|
|
||||||
kind: ModifierKind::Additive,
|
kind: ModifierKind::Additive,
|
||||||
instance,
|
instance,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
data.duration,
|
BuffKind::EnergyRegen => vec![BuffEffect::EnergyChangeOverTime {
|
||||||
),
|
|
||||||
BuffKind::EnergyRegen => (
|
|
||||||
vec![BuffEffect::EnergyChangeOverTime {
|
|
||||||
rate: data.strength,
|
rate: data.strength,
|
||||||
accumulated: 0.0,
|
|
||||||
kind: ModifierKind::Additive,
|
kind: ModifierKind::Additive,
|
||||||
}],
|
}],
|
||||||
data.duration,
|
BuffKind::IncreaseMaxEnergy => vec![BuffEffect::MaxEnergyModifier {
|
||||||
),
|
|
||||||
BuffKind::IncreaseMaxEnergy => (
|
|
||||||
vec![BuffEffect::MaxEnergyModifier {
|
|
||||||
value: data.strength,
|
value: data.strength,
|
||||||
kind: ModifierKind::Additive,
|
kind: ModifierKind::Additive,
|
||||||
}],
|
}],
|
||||||
data.duration,
|
BuffKind::IncreaseMaxHealth => vec![BuffEffect::MaxHealthModifier {
|
||||||
),
|
|
||||||
BuffKind::IncreaseMaxHealth => (
|
|
||||||
vec![BuffEffect::MaxHealthModifier {
|
|
||||||
value: data.strength,
|
value: data.strength,
|
||||||
kind: ModifierKind::Additive,
|
kind: ModifierKind::Additive,
|
||||||
}],
|
}],
|
||||||
data.duration,
|
BuffKind::Invulnerability => vec![BuffEffect::DamageReduction(1.0)],
|
||||||
),
|
BuffKind::ProtectingWard => vec![BuffEffect::DamageReduction(
|
||||||
BuffKind::Invulnerability => (vec![BuffEffect::DamageReduction(1.0)], data.duration),
|
|
||||||
BuffKind::ProtectingWard => (
|
|
||||||
vec![BuffEffect::DamageReduction(
|
|
||||||
// Causes non-linearity in effect strength, but necessary
|
// Causes non-linearity in effect strength, but necessary
|
||||||
// to allow for tool power and other things to affect the
|
// to allow for tool power and other things to affect the
|
||||||
// strength. 0.5 also still provides 50% damage reduction.
|
// strength. 0.5 also still provides 50% damage reduction.
|
||||||
nn_scaling(data.strength),
|
nn_scaling(data.strength),
|
||||||
)],
|
)],
|
||||||
data.duration,
|
BuffKind::Burning => vec![BuffEffect::HealthChangeOverTime {
|
||||||
),
|
|
||||||
BuffKind::Burning => (
|
|
||||||
vec![BuffEffect::HealthChangeOverTime {
|
|
||||||
rate: -data.strength,
|
rate: -data.strength,
|
||||||
accumulated: 0.0,
|
|
||||||
kind: ModifierKind::Additive,
|
kind: ModifierKind::Additive,
|
||||||
instance,
|
instance,
|
||||||
}],
|
}],
|
||||||
data.duration,
|
BuffKind::Poisoned => vec![BuffEffect::EnergyChangeOverTime {
|
||||||
),
|
|
||||||
BuffKind::Poisoned => (
|
|
||||||
vec![BuffEffect::EnergyChangeOverTime {
|
|
||||||
rate: -data.strength,
|
rate: -data.strength,
|
||||||
accumulated: 0.0,
|
|
||||||
kind: ModifierKind::Additive,
|
kind: ModifierKind::Additive,
|
||||||
}],
|
}],
|
||||||
data.duration,
|
BuffKind::Crippled => vec![
|
||||||
),
|
|
||||||
BuffKind::Crippled => (
|
|
||||||
vec![
|
|
||||||
BuffEffect::MovementSpeed(1.0 - nn_scaling(data.strength)),
|
BuffEffect::MovementSpeed(1.0 - nn_scaling(data.strength)),
|
||||||
BuffEffect::HealthChangeOverTime {
|
BuffEffect::HealthChangeOverTime {
|
||||||
rate: -data.strength * 4.0,
|
rate: -data.strength * 4.0,
|
||||||
accumulated: 0.0,
|
|
||||||
kind: ModifierKind::Additive,
|
kind: ModifierKind::Additive,
|
||||||
instance,
|
instance,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
data.duration,
|
BuffKind::Frenzied => vec![
|
||||||
),
|
|
||||||
BuffKind::Frenzied => (
|
|
||||||
vec![
|
|
||||||
BuffEffect::MovementSpeed(1.0 + data.strength),
|
BuffEffect::MovementSpeed(1.0 + data.strength),
|
||||||
BuffEffect::HealthChangeOverTime {
|
BuffEffect::HealthChangeOverTime {
|
||||||
rate: data.strength * 10.0,
|
rate: data.strength * 10.0,
|
||||||
accumulated: 0.0,
|
|
||||||
kind: ModifierKind::Additive,
|
kind: ModifierKind::Additive,
|
||||||
instance,
|
instance,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
data.duration,
|
BuffKind::Frozen => vec![
|
||||||
),
|
|
||||||
BuffKind::Frozen => (
|
|
||||||
vec![
|
|
||||||
BuffEffect::MovementSpeed(f32::powf(1.0 - nn_scaling(data.strength), 1.1)),
|
BuffEffect::MovementSpeed(f32::powf(1.0 - nn_scaling(data.strength), 1.1)),
|
||||||
BuffEffect::AttackSpeed(1.0 - nn_scaling(data.strength)),
|
BuffEffect::AttackSpeed(1.0 - nn_scaling(data.strength)),
|
||||||
],
|
],
|
||||||
data.duration,
|
BuffKind::Wet => vec![BuffEffect::GroundFriction(1.0 - nn_scaling(data.strength))],
|
||||||
),
|
BuffKind::Ensnared => vec![BuffEffect::MovementSpeed(1.0 - nn_scaling(data.strength))],
|
||||||
BuffKind::Wet => (
|
BuffKind::Hastened => vec![
|
||||||
vec![BuffEffect::GroundFriction(1.0 - nn_scaling(data.strength))],
|
|
||||||
data.duration,
|
|
||||||
),
|
|
||||||
BuffKind::Ensnared => (
|
|
||||||
vec![BuffEffect::MovementSpeed(1.0 - nn_scaling(data.strength))],
|
|
||||||
data.duration,
|
|
||||||
),
|
|
||||||
BuffKind::Hastened => (
|
|
||||||
vec![
|
|
||||||
BuffEffect::MovementSpeed(1.0 + data.strength),
|
BuffEffect::MovementSpeed(1.0 + data.strength),
|
||||||
BuffEffect::AttackSpeed(1.0 + data.strength),
|
BuffEffect::AttackSpeed(1.0 + data.strength),
|
||||||
],
|
],
|
||||||
data.duration,
|
BuffKind::Fortitude => vec![BuffEffect::PoiseReduction(data.strength)],
|
||||||
),
|
BuffKind::Parried => vec![BuffEffect::AttackSpeed(0.5)],
|
||||||
BuffKind::Fortitude => (
|
BuffKind::PotionSickness => vec![BuffEffect::HealReduction(data.strength)],
|
||||||
vec![BuffEffect::PoiseReduction(data.strength)],
|
};
|
||||||
data.duration,
|
let start_time = Time(time.0 + data.delay.map_or(0.0, |delay| delay.0));
|
||||||
),
|
let end_time = if cat_ids
|
||||||
BuffKind::Parried => (vec![BuffEffect::AttackSpeed(0.5)], data.duration),
|
.iter()
|
||||||
BuffKind::PotionSickness => (
|
.any(|cat_id| matches!(cat_id, BuffCategory::FromActiveAura(..)))
|
||||||
vec![BuffEffect::HealReduction {
|
{
|
||||||
rate: data.strength,
|
None
|
||||||
}],
|
} else {
|
||||||
data.duration,
|
data.duration.map(|dur| Time(start_time.0 + dur.0))
|
||||||
),
|
|
||||||
};
|
};
|
||||||
Buff {
|
Buff {
|
||||||
kind,
|
kind,
|
||||||
data,
|
data,
|
||||||
cat_ids,
|
cat_ids,
|
||||||
time,
|
start_time,
|
||||||
delay: data.delay,
|
end_time,
|
||||||
effects,
|
effects,
|
||||||
source,
|
source,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Calculate how much time has elapsed since the buff was applied (if the
|
/// Calculate how much time has elapsed since the buff was applied
|
||||||
/// buff has a finite duration, otherwise insufficient information
|
pub fn elapsed(&self, time: Time) -> Secs { Secs(time.0 - self.start_time.0) }
|
||||||
/// exists to track that)
|
|
||||||
pub fn elapsed(&self) -> Option<Duration> {
|
|
||||||
self.data.duration.zip_with(self.time, |x, y| x - y)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
@ -454,9 +402,13 @@ impl PartialOrd for Buff {
|
|||||||
Some(Ordering::Greater)
|
Some(Ordering::Greater)
|
||||||
} else if self.data.strength < other.data.strength {
|
} else if self.data.strength < other.data.strength {
|
||||||
Some(Ordering::Less)
|
Some(Ordering::Less)
|
||||||
} else if compare_duration(self.time, other.time) {
|
} else if self.data.delay.is_none() && other.data.delay.is_some() {
|
||||||
Some(Ordering::Greater)
|
Some(Ordering::Greater)
|
||||||
} else if compare_duration(other.time, self.time) {
|
} else if self.data.delay.is_some() && other.data.delay.is_none() {
|
||||||
|
Some(Ordering::Less)
|
||||||
|
} else if compare_end_time(self.end_time, other.end_time) {
|
||||||
|
Some(Ordering::Greater)
|
||||||
|
} else if compare_end_time(other.end_time, self.end_time) {
|
||||||
Some(Ordering::Less)
|
Some(Ordering::Less)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
@ -465,14 +417,16 @@ impl PartialOrd for Buff {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
fn compare_duration(a: Option<Duration>, b: Option<Duration>) -> bool {
|
fn compare_end_time(a: Option<Time>, b: Option<Time>) -> bool {
|
||||||
a.map_or(true, |dur_a| b.map_or(false, |dur_b| dur_a > dur_b))
|
a.map_or(true, |time_a| b.map_or(false, |time_b| time_a.0 > time_b.0))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
impl PartialEq for Buff {
|
impl PartialEq for Buff {
|
||||||
fn eq(&self, other: &Self) -> bool {
|
fn eq(&self, other: &Self) -> bool {
|
||||||
self.data.strength == other.data.strength && self.time == other.time
|
self.data.strength == other.data.strength
|
||||||
|
&& self.end_time == other.end_time
|
||||||
|
&& self.start_time == other.start_time
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -509,8 +463,10 @@ pub enum BuffSource {
|
|||||||
pub struct Buffs {
|
pub struct Buffs {
|
||||||
/// Uid used for synchronization
|
/// Uid used for synchronization
|
||||||
id_counter: u64,
|
id_counter: u64,
|
||||||
/// Maps Kinds of buff to Id's of currently applied buffs of that kind
|
/// Maps Kinds of buff to Id's of currently applied buffs of that kind and
|
||||||
pub kinds: HashMap<BuffKind, Vec<BuffId>>,
|
/// the time that the first buff was added (time gets reset if entity no
|
||||||
|
/// longer has buffs of that kind)
|
||||||
|
pub kinds: HashMap<BuffKind, (Vec<BuffId>, Time)>,
|
||||||
// All currently applied buffs stored by Id
|
// All currently applied buffs stored by Id
|
||||||
pub buffs: HashMap<BuffId, Buff>,
|
pub buffs: HashMap<BuffId, Buff>,
|
||||||
}
|
}
|
||||||
@ -519,13 +475,14 @@ pub struct Buffs {
|
|||||||
impl Buffs {
|
impl Buffs {
|
||||||
fn sort_kind(&mut self, kind: BuffKind) {
|
fn sort_kind(&mut self, kind: BuffKind) {
|
||||||
if let Some(buff_order) = self.kinds.get_mut(&kind) {
|
if let Some(buff_order) = self.kinds.get_mut(&kind) {
|
||||||
if buff_order.is_empty() {
|
if buff_order.0.is_empty() {
|
||||||
self.kinds.remove(&kind);
|
self.kinds.remove(&kind);
|
||||||
} else {
|
} else {
|
||||||
let buffs = &self.buffs;
|
let buffs = &self.buffs;
|
||||||
// Intentionally sorted in reverse so that the strongest buffs are earlier in
|
// Intentionally sorted in reverse so that the strongest buffs are earlier in
|
||||||
// the vector
|
// the vector
|
||||||
buff_order
|
buff_order
|
||||||
|
.0
|
||||||
.sort_by(|a, b| buffs[b].partial_cmp(&buffs[a]).unwrap_or(Ordering::Equal));
|
.sort_by(|a, b| buffs[b].partial_cmp(&buffs[a]).unwrap_or(Ordering::Equal));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -533,24 +490,31 @@ impl Buffs {
|
|||||||
|
|
||||||
pub fn remove_kind(&mut self, kind: BuffKind) {
|
pub fn remove_kind(&mut self, kind: BuffKind) {
|
||||||
if let Some(buff_ids) = self.kinds.get_mut(&kind) {
|
if let Some(buff_ids) = self.kinds.get_mut(&kind) {
|
||||||
for id in buff_ids {
|
for id in &buff_ids.0 {
|
||||||
self.buffs.remove(id);
|
self.buffs.remove(id);
|
||||||
}
|
}
|
||||||
self.kinds.remove(&kind);
|
self.kinds.remove(&kind);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn force_insert(&mut self, id: BuffId, buff: Buff) -> BuffId {
|
fn force_insert(&mut self, id: BuffId, buff: Buff, current_time: Time) -> BuffId {
|
||||||
let kind = buff.kind;
|
let kind = buff.kind;
|
||||||
self.kinds.entry(kind).or_default().push(id);
|
self.kinds
|
||||||
|
.entry(kind)
|
||||||
|
.or_insert((Vec::new(), current_time))
|
||||||
|
.0
|
||||||
|
.push(id);
|
||||||
self.buffs.insert(id, buff);
|
self.buffs.insert(id, buff);
|
||||||
self.sort_kind(kind);
|
self.sort_kind(kind);
|
||||||
|
if kind.queues() {
|
||||||
|
self.delay_queueable_buffs(kind, current_time);
|
||||||
|
}
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn insert(&mut self, buff: Buff) -> BuffId {
|
pub fn insert(&mut self, buff: Buff, current_time: Time) -> BuffId {
|
||||||
self.id_counter += 1;
|
self.id_counter += 1;
|
||||||
self.force_insert(self.id_counter, buff)
|
self.force_insert(self.id_counter, buff, current_time)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn contains(&self, kind: BuffKind) -> bool { self.kinds.contains_key(&kind) }
|
pub fn contains(&self, kind: BuffKind) -> bool { self.kinds.contains_key(&kind) }
|
||||||
@ -559,7 +523,7 @@ impl Buffs {
|
|||||||
pub fn iter_kind(&self, kind: BuffKind) -> impl Iterator<Item = (BuffId, &Buff)> + '_ {
|
pub fn iter_kind(&self, kind: BuffKind) -> impl Iterator<Item = (BuffId, &Buff)> + '_ {
|
||||||
self.kinds
|
self.kinds
|
||||||
.get(&kind)
|
.get(&kind)
|
||||||
.map(|ids| ids.iter())
|
.map(|ids| ids.0.iter())
|
||||||
.unwrap_or_else(|| [].iter())
|
.unwrap_or_else(|| [].iter())
|
||||||
.map(move |id| (*id, &self.buffs[id]))
|
.map(move |id| (*id, &self.buffs[id]))
|
||||||
}
|
}
|
||||||
@ -571,29 +535,52 @@ impl Buffs {
|
|||||||
if kind.stacks() {
|
if kind.stacks() {
|
||||||
// Iterate stackable buffs in reverse order to show the timer of the soonest one
|
// Iterate stackable buffs in reverse order to show the timer of the soonest one
|
||||||
// to expire
|
// to expire
|
||||||
Either::Left(ids.iter().filter_map(|id| self.buffs.get(id)).rev())
|
Either::Left(ids.0.iter().filter_map(|id| self.buffs.get(id)).rev())
|
||||||
} else {
|
} else {
|
||||||
Either::Right(self.buffs.get(&ids[0]).into_iter())
|
Either::Right(self.buffs.get(&ids.0[0]).into_iter())
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gets most powerful buff of a given kind
|
// Gets most powerful buff of a given kind
|
||||||
// pub fn get_active_kind(&self, kind: BuffKind) -> Buff
|
|
||||||
pub fn remove(&mut self, buff_id: BuffId) {
|
pub fn remove(&mut self, buff_id: BuffId) {
|
||||||
if let Some(kind) = self.buffs.remove(&buff_id) {
|
if let Some(kind) = self.buffs.remove(&buff_id) {
|
||||||
let kind = kind.kind;
|
let kind = kind.kind;
|
||||||
self.kinds
|
self.kinds
|
||||||
.get_mut(&kind)
|
.get_mut(&kind)
|
||||||
.map(|ids| ids.retain(|id| *id != buff_id));
|
.map(|ids| ids.0.retain(|id| *id != buff_id));
|
||||||
self.sort_kind(kind);
|
self.sort_kind(kind);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns an immutable reference to the buff kinds on an entity, and a
|
fn delay_queueable_buffs(&mut self, kind: BuffKind, current_time: Time) {
|
||||||
/// mutable reference to the buffs
|
let mut next_start_time: Option<Time> = None;
|
||||||
pub fn parts(&mut self) -> (&HashMap<BuffKind, Vec<BuffId>>, &mut HashMap<BuffId, Buff>) {
|
debug_assert!(kind.queues());
|
||||||
(&self.kinds, &mut self.buffs)
|
if let Some(buffs) = self.kinds.get(&kind) {
|
||||||
|
buffs.0.iter().for_each(|id| {
|
||||||
|
if let Some(buff) = self.buffs.get_mut(id) {
|
||||||
|
// End time only being updated when there is some next_start_time will
|
||||||
|
// technically cause buffs to "end early" if they have a weaker strength than a
|
||||||
|
// buff with an infinite duration, but this is fine since those buffs wouldn't
|
||||||
|
// matter anyways
|
||||||
|
if let Some(next_start_time) = next_start_time {
|
||||||
|
// Delays buff so that it has the same progress it has now at the time the
|
||||||
|
// previous buff would end.
|
||||||
|
//
|
||||||
|
// Shift should be relative to current time, unless the buff is delayed and
|
||||||
|
// hasn't started yet
|
||||||
|
let reference_time = current_time.0.max(buff.start_time.0);
|
||||||
|
// If buff has a delay, ensure that queueables shuffling queue does not
|
||||||
|
// potentially allow skipping delay
|
||||||
|
buff.start_time = Time(next_start_time.0.max(buff.start_time.0));
|
||||||
|
buff.end_time = buff.end_time.map(|end| {
|
||||||
|
Time(end.0 + next_start_time.0.max(reference_time) - reference_time)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
next_start_time = buff.end_time;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -603,3 +590,129 @@ pub type BuffId = u64;
|
|||||||
impl Component for Buffs {
|
impl Component for Buffs {
|
||||||
type Storage = DerefFlaggedStorage<Self, VecStorage<Self>>;
|
type Storage = DerefFlaggedStorage<Self, VecStorage<Self>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
pub mod tests {
|
||||||
|
use crate::comp::buff::*;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
fn create_test_queueable_buff(buff_data: BuffData, time: Time) -> Buff {
|
||||||
|
// Change to another buff that queues if we ever add one and remove saturation,
|
||||||
|
// otherwise maybe add a test buff kind?
|
||||||
|
debug_assert!(BuffKind::Saturation.queues());
|
||||||
|
Buff::new(
|
||||||
|
BuffKind::Saturation,
|
||||||
|
buff_data,
|
||||||
|
Vec::new(),
|
||||||
|
BuffSource::Unknown,
|
||||||
|
time,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
/// Tests a number of buffs with various progresses that queue to ensure
|
||||||
|
/// queue has correct total duration
|
||||||
|
fn test_queueable_buffs_three() {
|
||||||
|
let mut buff_comp: Buffs = Default::default();
|
||||||
|
let buff_data = BuffData::new(1.0, Some(Secs(10.0)), None);
|
||||||
|
let time_a = Time(0.0);
|
||||||
|
buff_comp.insert(create_test_queueable_buff(buff_data, time_a), time_a);
|
||||||
|
let time_b = Time(6.0);
|
||||||
|
buff_comp.insert(create_test_queueable_buff(buff_data, time_b), time_b);
|
||||||
|
let time_c = Time(11.0);
|
||||||
|
buff_comp.insert(create_test_queueable_buff(buff_data, time_c), time_c);
|
||||||
|
// Check that all buffs have an end_time less than or equal to 30, and that at
|
||||||
|
// least one has an end_time greater than or equal to 30.
|
||||||
|
//
|
||||||
|
// This should be true because 3 buffs that each lasted for 10 seconds were
|
||||||
|
// inserted at various times, so the total duration should be 30 seconds.
|
||||||
|
assert!(
|
||||||
|
buff_comp
|
||||||
|
.buffs
|
||||||
|
.values()
|
||||||
|
.all(|b| b.end_time.unwrap().0 < 30.01)
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
buff_comp
|
||||||
|
.buffs
|
||||||
|
.values()
|
||||||
|
.any(|b| b.end_time.unwrap().0 > 29.99)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
/// Tests that if a buff had a delay but will start soon, and an immediate
|
||||||
|
/// queueable buff is added, delayed buff has correct start time
|
||||||
|
fn test_queueable_buff_delay_start() {
|
||||||
|
let mut buff_comp: Buffs = Default::default();
|
||||||
|
let queued_buff_data = BuffData::new(1.0, Some(Secs(10.0)), Some(Secs(10.0)));
|
||||||
|
let buff_data = BuffData::new(1.0, Some(Secs(10.0)), None);
|
||||||
|
let time_a = Time(0.0);
|
||||||
|
buff_comp.insert(create_test_queueable_buff(queued_buff_data, time_a), time_a);
|
||||||
|
let time_b = Time(6.0);
|
||||||
|
buff_comp.insert(create_test_queueable_buff(buff_data, time_b), time_b);
|
||||||
|
// Check that all buffs have an end_time less than or equal to 26, and that at
|
||||||
|
// least one has an end_time greater than or equal to 26.
|
||||||
|
//
|
||||||
|
// This should be true because the first buff added had a delay of 10 seconds
|
||||||
|
// and a duration of 10 seconds, the second buff added at 6 seconds had no
|
||||||
|
// delay, and a duration of 10 seconds. When it finishes at 16 seconds the first
|
||||||
|
// buff is past the delay time so should finish at 26 seconds.
|
||||||
|
assert!(
|
||||||
|
buff_comp
|
||||||
|
.buffs
|
||||||
|
.values()
|
||||||
|
.all(|b| b.end_time.unwrap().0 < 26.01)
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
buff_comp
|
||||||
|
.buffs
|
||||||
|
.values()
|
||||||
|
.any(|b| b.end_time.unwrap().0 > 25.99)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
/// Tests that if a buff had a long delay, a short immediate queueable buff
|
||||||
|
/// does not move delayed buff start or end times
|
||||||
|
fn test_queueable_buff_long_delay() {
|
||||||
|
let mut buff_comp: Buffs = Default::default();
|
||||||
|
let queued_buff_data = BuffData::new(1.0, Some(Secs(10.0)), Some(Secs(50.0)));
|
||||||
|
let buff_data = BuffData::new(1.0, Some(Secs(10.0)), None);
|
||||||
|
let time_a = Time(0.0);
|
||||||
|
buff_comp.insert(create_test_queueable_buff(queued_buff_data, time_a), time_a);
|
||||||
|
let time_b = Time(10.0);
|
||||||
|
buff_comp.insert(create_test_queueable_buff(buff_data, time_b), time_b);
|
||||||
|
// Check that all buffs have either an end time less than or equal to 20 seconds
|
||||||
|
// XOR a start time greater than or equal to 50 seconds, that all buffs have a
|
||||||
|
// start time less than or equal to 50 seconds, that all buffs have an end time
|
||||||
|
// less than or equal to 60 seconds, and that at least one buff has an end time
|
||||||
|
// greater than or equal to 60 seconds
|
||||||
|
//
|
||||||
|
// This should be true because the first buff has a delay of 50 seconds, the
|
||||||
|
// second buff added has no delay at 10 seconds and lasts 10 seconds, so should
|
||||||
|
// end at 20 seconds and not affect the start time of the delayed buff, and
|
||||||
|
// since the delayed buff was not affected the end time should be 10 seconds
|
||||||
|
// after the start time: 60 seconds != used here to emulate xor
|
||||||
|
assert!(
|
||||||
|
buff_comp
|
||||||
|
.buffs
|
||||||
|
.values()
|
||||||
|
.all(|b| (b.end_time.unwrap().0 < 20.01) != (b.start_time.0 > 49.99))
|
||||||
|
);
|
||||||
|
assert!(buff_comp.buffs.values().all(|b| b.start_time.0 < 50.01));
|
||||||
|
assert!(
|
||||||
|
buff_comp
|
||||||
|
.buffs
|
||||||
|
.values()
|
||||||
|
.all(|b| b.end_time.unwrap().0 < 60.01)
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
buff_comp
|
||||||
|
.buffs
|
||||||
|
.values()
|
||||||
|
.any(|b| b.end_time.unwrap().0 > 59.99)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -3,6 +3,7 @@ use crate::comp::Pos;
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
use specs::Entity;
|
use specs::Entity;
|
||||||
|
use std::ops::{Mul, MulAssign};
|
||||||
|
|
||||||
/// A resource that stores the time of day.
|
/// A resource that stores the time of day.
|
||||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, Default)]
|
#[derive(Copy, Clone, Debug, Serialize, Deserialize, Default)]
|
||||||
@ -16,6 +17,20 @@ pub struct Time(pub f64);
|
|||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct DeltaTime(pub f32);
|
pub struct DeltaTime(pub f32);
|
||||||
|
|
||||||
|
/// A resource used to indicate a duration of time, in seconds
|
||||||
|
#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize, PartialEq)]
|
||||||
|
#[serde(transparent)]
|
||||||
|
pub struct Secs(pub f64);
|
||||||
|
|
||||||
|
impl Mul<f64> for Secs {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn mul(self, mult: f64) -> Self { Self(self.0 * mult) }
|
||||||
|
}
|
||||||
|
impl MulAssign<f64> for Secs {
|
||||||
|
fn mul_assign(&mut self, mult: f64) { *self = *self * mult; }
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct EntitiesDiedLastTick(pub Vec<(Entity, Pos)>);
|
pub struct EntitiesDiedLastTick(pub Vec<(Entity, Pos)>);
|
||||||
|
@ -6,6 +6,7 @@ use crate::{
|
|||||||
CharacterState, StateUpdate,
|
CharacterState, StateUpdate,
|
||||||
},
|
},
|
||||||
event::ServerEvent,
|
event::ServerEvent,
|
||||||
|
resources::Secs,
|
||||||
states::{
|
states::{
|
||||||
behavior::{CharacterBehavior, JoinData},
|
behavior::{CharacterBehavior, JoinData},
|
||||||
utils::*,
|
utils::*,
|
||||||
@ -28,7 +29,7 @@ pub struct StaticData {
|
|||||||
/// Has information used to construct the auras
|
/// Has information used to construct the auras
|
||||||
pub auras: Vec<AuraBuffConstructor>,
|
pub auras: Vec<AuraBuffConstructor>,
|
||||||
/// How long aura lasts
|
/// How long aura lasts
|
||||||
pub aura_duration: Duration,
|
pub aura_duration: Secs,
|
||||||
/// Radius of aura
|
/// Radius of aura
|
||||||
pub range: f32,
|
pub range: f32,
|
||||||
/// What key is used to press ability
|
/// What key is used to press ability
|
||||||
@ -79,6 +80,7 @@ impl CharacterBehavior for Data {
|
|||||||
self.static_data.range,
|
self.static_data.range,
|
||||||
Some(self.static_data.aura_duration),
|
Some(self.static_data.aura_duration),
|
||||||
targets,
|
targets,
|
||||||
|
*data.time,
|
||||||
);
|
);
|
||||||
if self.static_data.scales_with_combo {
|
if self.static_data.scales_with_combo {
|
||||||
match aura.aura_kind {
|
match aura.aura_kind {
|
||||||
|
@ -9,7 +9,7 @@ use crate::{
|
|||||||
},
|
},
|
||||||
link::Is,
|
link::Is,
|
||||||
mounting::Rider,
|
mounting::Rider,
|
||||||
resources::DeltaTime,
|
resources::{DeltaTime, Time},
|
||||||
terrain::TerrainGrid,
|
terrain::TerrainGrid,
|
||||||
uid::Uid,
|
uid::Uid,
|
||||||
};
|
};
|
||||||
@ -125,6 +125,7 @@ pub struct JoinData<'a> {
|
|||||||
pub mass: &'a Mass,
|
pub mass: &'a Mass,
|
||||||
pub density: &'a Density,
|
pub density: &'a Density,
|
||||||
pub dt: &'a DeltaTime,
|
pub dt: &'a DeltaTime,
|
||||||
|
pub time: &'a Time,
|
||||||
pub controller: &'a Controller,
|
pub controller: &'a Controller,
|
||||||
pub inputs: &'a ControllerInputs,
|
pub inputs: &'a ControllerInputs,
|
||||||
pub health: Option<&'a Health>,
|
pub health: Option<&'a Health>,
|
||||||
@ -176,6 +177,7 @@ impl<'a> JoinData<'a> {
|
|||||||
j: &'a JoinStruct<'a>,
|
j: &'a JoinStruct<'a>,
|
||||||
updater: &'a LazyUpdate,
|
updater: &'a LazyUpdate,
|
||||||
dt: &'a DeltaTime,
|
dt: &'a DeltaTime,
|
||||||
|
time: &'a Time,
|
||||||
msm: &'a MaterialStatManifest,
|
msm: &'a MaterialStatManifest,
|
||||||
ability_map: &'a AbilityMap,
|
ability_map: &'a AbilityMap,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
@ -200,6 +202,7 @@ impl<'a> JoinData<'a> {
|
|||||||
skill_set: j.skill_set,
|
skill_set: j.skill_set,
|
||||||
updater,
|
updater,
|
||||||
dt,
|
dt,
|
||||||
|
time,
|
||||||
msm,
|
msm,
|
||||||
ability_map,
|
ability_map,
|
||||||
combo: j.combo,
|
combo: j.combo,
|
||||||
|
@ -5,6 +5,7 @@ use crate::{
|
|||||||
CharacterState, StateUpdate,
|
CharacterState, StateUpdate,
|
||||||
},
|
},
|
||||||
event::ServerEvent,
|
event::ServerEvent,
|
||||||
|
resources::Secs,
|
||||||
states::{
|
states::{
|
||||||
behavior::{CharacterBehavior, JoinData},
|
behavior::{CharacterBehavior, JoinData},
|
||||||
utils::*,
|
utils::*,
|
||||||
@ -27,7 +28,7 @@ pub struct StaticData {
|
|||||||
/// Strength of the created buff
|
/// Strength of the created buff
|
||||||
pub buff_strength: f32,
|
pub buff_strength: f32,
|
||||||
/// How long buff lasts
|
/// How long buff lasts
|
||||||
pub buff_duration: Option<Duration>,
|
pub buff_duration: Option<Secs>,
|
||||||
/// What key is used to press ability
|
/// What key is used to press ability
|
||||||
pub ability_info: AbilityInfo,
|
pub ability_info: AbilityInfo,
|
||||||
}
|
}
|
||||||
@ -69,6 +70,8 @@ impl CharacterBehavior for Data {
|
|||||||
},
|
},
|
||||||
Vec::new(),
|
Vec::new(),
|
||||||
BuffSource::Character { by: *data.uid },
|
BuffSource::Character { by: *data.uid },
|
||||||
|
*data.time,
|
||||||
|
Some(data.stats),
|
||||||
);
|
);
|
||||||
output_events.emit_server(ServerEvent::Buff {
|
output_events.emit_server(ServerEvent::Buff {
|
||||||
entity: data.entity,
|
entity: data.entity,
|
||||||
|
@ -75,6 +75,7 @@ impl BlockChange {
|
|||||||
pub struct ScheduledBlockChange {
|
pub struct ScheduledBlockChange {
|
||||||
changes: TimerQueue<HashMap<Vec3<i32>, Block>>,
|
changes: TimerQueue<HashMap<Vec3<i32>, Block>>,
|
||||||
outcomes: TimerQueue<HashMap<Vec3<i32>, Block>>,
|
outcomes: TimerQueue<HashMap<Vec3<i32>, Block>>,
|
||||||
|
last_poll_time: u64,
|
||||||
}
|
}
|
||||||
impl ScheduledBlockChange {
|
impl ScheduledBlockChange {
|
||||||
pub fn set(&mut self, pos: Vec3<i32>, block: Block, replace_time: f64) {
|
pub fn set(&mut self, pos: Vec3<i32>, block: Block, replace_time: f64) {
|
||||||
@ -252,9 +253,9 @@ impl State {
|
|||||||
ecs.insert(TimeOfDay(0.0));
|
ecs.insert(TimeOfDay(0.0));
|
||||||
ecs.insert(Calendar::default());
|
ecs.insert(Calendar::default());
|
||||||
ecs.insert(WeatherGrid::new(Vec2::zero()));
|
ecs.insert(WeatherGrid::new(Vec2::zero()));
|
||||||
|
ecs.insert(Time(0.0));
|
||||||
|
|
||||||
// Register unsynced resources used by the ECS.
|
// Register unsynced resources used by the ECS.
|
||||||
ecs.insert(Time(0.0));
|
|
||||||
ecs.insert(DeltaTime(0.0));
|
ecs.insert(DeltaTime(0.0));
|
||||||
ecs.insert(PlayerEntity(None));
|
ecs.insert(PlayerEntity(None));
|
||||||
ecs.insert(TerrainGrid::new(map_size_lg, default_chunk).unwrap());
|
ecs.insert(TerrainGrid::new(map_size_lg, default_chunk).unwrap());
|
||||||
@ -546,11 +547,18 @@ impl State {
|
|||||||
|
|
||||||
let mut scheduled_changes = self.ecs.write_resource::<ScheduledBlockChange>();
|
let mut scheduled_changes = self.ecs.write_resource::<ScheduledBlockChange>();
|
||||||
let current_time: f64 = self.ecs.read_resource::<Time>().0 * SECONDS_TO_MILLISECONDS;
|
let current_time: f64 = self.ecs.read_resource::<Time>().0 * SECONDS_TO_MILLISECONDS;
|
||||||
while let Some(changes) = scheduled_changes.changes.poll(current_time as u64) {
|
let current_time = current_time as u64;
|
||||||
|
// This is important as the poll function has a debug assert that the new poll
|
||||||
|
// is at a more recent time than the old poll. As Time is synced between server
|
||||||
|
// and client, there is a chance that client dt can get slightly ahead of a
|
||||||
|
// server update, so we do not want to panic in that scenario.
|
||||||
|
if scheduled_changes.last_poll_time < current_time {
|
||||||
|
scheduled_changes.last_poll_time = current_time;
|
||||||
|
while let Some(changes) = scheduled_changes.changes.poll(current_time) {
|
||||||
modified_blocks.extend(changes.iter());
|
modified_blocks.extend(changes.iter());
|
||||||
}
|
}
|
||||||
let outcome = self.ecs.read_resource::<EventBus<Outcome>>();
|
let outcome = self.ecs.read_resource::<EventBus<Outcome>>();
|
||||||
while let Some(outcomes) = scheduled_changes.outcomes.poll(current_time as u64) {
|
while let Some(outcomes) = scheduled_changes.outcomes.poll(current_time) {
|
||||||
for (pos, block) in outcomes.iter() {
|
for (pos, block) in outcomes.iter() {
|
||||||
let offset_dir = Vec3::<i32>::zero() - pos;
|
let offset_dir = Vec3::<i32>::zero() - pos;
|
||||||
let offset = offset_dir
|
let offset = offset_dir
|
||||||
@ -565,6 +573,7 @@ impl State {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// Apply block modifications
|
// Apply block modifications
|
||||||
// Only include in `TerrainChanges` if successful
|
// Only include in `TerrainChanges` if successful
|
||||||
modified_blocks.retain(|pos, block| {
|
modified_blocks.retain(|pos, block| {
|
||||||
|
@ -4,24 +4,23 @@ use common::{
|
|||||||
aura::{AuraChange, AuraKey, AuraKind, AuraTarget},
|
aura::{AuraChange, AuraKey, AuraKind, AuraTarget},
|
||||||
buff::{Buff, BuffCategory, BuffChange, BuffSource},
|
buff::{Buff, BuffCategory, BuffChange, BuffSource},
|
||||||
group::Group,
|
group::Group,
|
||||||
Alignment, Aura, Auras, BuffKind, Buffs, CharacterState, Health, Player, Pos,
|
Alignment, Aura, Auras, BuffKind, Buffs, CharacterState, Health, Player, Pos, Stats,
|
||||||
},
|
},
|
||||||
event::{Emitter, EventBus, ServerEvent},
|
event::{Emitter, EventBus, ServerEvent},
|
||||||
resources::DeltaTime,
|
resources::Time,
|
||||||
uid::{Uid, UidAllocator},
|
uid::{Uid, UidAllocator},
|
||||||
};
|
};
|
||||||
use common_ecs::{Job, Origin, Phase, System};
|
use common_ecs::{Job, Origin, Phase, System};
|
||||||
use specs::{
|
use specs::{
|
||||||
saveload::MarkerAllocator, shred::ResourceId, Entities, Entity as EcsEntity, Join, Read,
|
saveload::MarkerAllocator, shred::ResourceId, Entities, Entity as EcsEntity, Join, Read,
|
||||||
ReadStorage, SystemData, World, WriteStorage,
|
ReadStorage, SystemData, World,
|
||||||
};
|
};
|
||||||
use std::time::Duration;
|
|
||||||
|
|
||||||
#[derive(SystemData)]
|
#[derive(SystemData)]
|
||||||
pub struct ReadData<'a> {
|
pub struct ReadData<'a> {
|
||||||
entities: Entities<'a>,
|
entities: Entities<'a>,
|
||||||
players: ReadStorage<'a, Player>,
|
players: ReadStorage<'a, Player>,
|
||||||
dt: Read<'a, DeltaTime>,
|
time: Read<'a, Time>,
|
||||||
server_bus: Read<'a, EventBus<ServerEvent>>,
|
server_bus: Read<'a, EventBus<ServerEvent>>,
|
||||||
uid_allocator: Read<'a, UidAllocator>,
|
uid_allocator: Read<'a, UidAllocator>,
|
||||||
cached_spatial_grid: Read<'a, common::CachedSpatialGrid>,
|
cached_spatial_grid: Read<'a, common::CachedSpatialGrid>,
|
||||||
@ -31,57 +30,38 @@ pub struct ReadData<'a> {
|
|||||||
healths: ReadStorage<'a, Health>,
|
healths: ReadStorage<'a, Health>,
|
||||||
groups: ReadStorage<'a, Group>,
|
groups: ReadStorage<'a, Group>,
|
||||||
uids: ReadStorage<'a, Uid>,
|
uids: ReadStorage<'a, Uid>,
|
||||||
|
stats: ReadStorage<'a, Stats>,
|
||||||
|
buffs: ReadStorage<'a, Buffs>,
|
||||||
|
auras: ReadStorage<'a, Auras>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct Sys;
|
pub struct Sys;
|
||||||
impl<'a> System<'a> for Sys {
|
impl<'a> System<'a> for Sys {
|
||||||
type SystemData = (
|
type SystemData = ReadData<'a>;
|
||||||
ReadData<'a>,
|
|
||||||
WriteStorage<'a, Auras>,
|
|
||||||
WriteStorage<'a, Buffs>,
|
|
||||||
);
|
|
||||||
|
|
||||||
const NAME: &'static str = "aura";
|
const NAME: &'static str = "aura";
|
||||||
const ORIGIN: Origin = Origin::Common;
|
const ORIGIN: Origin = Origin::Common;
|
||||||
const PHASE: Phase = Phase::Create;
|
const PHASE: Phase = Phase::Create;
|
||||||
|
|
||||||
fn run(_job: &mut Job<Self>, (read_data, mut auras, mut buffs): Self::SystemData) {
|
fn run(_job: &mut Job<Self>, read_data: Self::SystemData) {
|
||||||
let mut server_emitter = read_data.server_bus.emitter();
|
let mut server_emitter = read_data.server_bus.emitter();
|
||||||
let dt = read_data.dt.0;
|
|
||||||
|
|
||||||
auras.set_event_emission(false);
|
|
||||||
buffs.set_event_emission(false);
|
|
||||||
|
|
||||||
// Iterate through all buffs, on any buffs that are from an aura, sets the check
|
|
||||||
// for whether the buff recently set by aura to false
|
|
||||||
for (_, mut buffs_comp) in (&read_data.entities, &mut buffs).join() {
|
|
||||||
for (_, buff) in buffs_comp.buffs.iter_mut() {
|
|
||||||
if let Some(cat_id) = buff
|
|
||||||
.cat_ids
|
|
||||||
.iter_mut()
|
|
||||||
.find(|cat_id| matches!(cat_id, BuffCategory::FromAura(true)))
|
|
||||||
{
|
|
||||||
*cat_id = BuffCategory::FromAura(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Iterate through all entities with an aura
|
// Iterate through all entities with an aura
|
||||||
for (entity, pos, mut auras_comp) in
|
for (entity, pos, auras_comp, uid) in (
|
||||||
(&read_data.entities, &read_data.positions, &mut auras).join()
|
&read_data.entities,
|
||||||
|
&read_data.positions,
|
||||||
|
&read_data.auras,
|
||||||
|
&read_data.uids,
|
||||||
|
)
|
||||||
|
.join()
|
||||||
{
|
{
|
||||||
let mut expired_auras = Vec::<AuraKey>::new();
|
let mut expired_auras = Vec::<AuraKey>::new();
|
||||||
// Iterate through the auras attached to this entity
|
// Iterate through the auras attached to this entity
|
||||||
for (key, aura) in auras_comp.auras.iter_mut() {
|
for (key, aura) in auras_comp.auras.iter() {
|
||||||
// Tick the aura and subtract dt from it
|
// Tick the aura and subtract dt from it
|
||||||
if let Some(remaining_time) = &mut aura.duration {
|
if let Some(end_time) = aura.end_time {
|
||||||
if let Some(new_duration) =
|
if read_data.time.0 > end_time.0 {
|
||||||
remaining_time.checked_sub(Duration::from_secs_f32(dt))
|
|
||||||
{
|
|
||||||
*remaining_time = new_duration;
|
|
||||||
} else {
|
|
||||||
*remaining_time = Duration::default();
|
|
||||||
expired_auras.push(key);
|
expired_auras.push(key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -96,11 +76,17 @@ impl<'a> System<'a> for Sys {
|
|||||||
.and_then(|l| read_data.healths.get(target).map(|r| (l, r)))
|
.and_then(|l| read_data.healths.get(target).map(|r| (l, r)))
|
||||||
.and_then(|l| read_data.uids.get(target).map(|r| (l, r)))
|
.and_then(|l| read_data.uids.get(target).map(|r| (l, r)))
|
||||||
.map(|((target_pos, health), target_uid)| {
|
.map(|((target_pos, health), target_uid)| {
|
||||||
(target, target_pos, health, target_uid)
|
(
|
||||||
|
target,
|
||||||
|
target_pos,
|
||||||
|
health,
|
||||||
|
target_uid,
|
||||||
|
read_data.stats.get(target),
|
||||||
|
)
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
target_iter.for_each(|(target, target_pos, health, target_uid)| {
|
target_iter.for_each(|(target, target_pos, health, target_uid, stats)| {
|
||||||
let mut target_buffs = match buffs.get_mut(target) {
|
let target_buffs = match read_data.buffs.get(target) {
|
||||||
Some(buff) => buff,
|
Some(buff) => buff,
|
||||||
None => return,
|
None => return,
|
||||||
};
|
};
|
||||||
@ -127,10 +113,13 @@ impl<'a> System<'a> for Sys {
|
|||||||
|
|
||||||
if is_target {
|
if is_target {
|
||||||
activate_aura(
|
activate_aura(
|
||||||
|
key,
|
||||||
aura,
|
aura,
|
||||||
|
*uid,
|
||||||
target,
|
target,
|
||||||
health,
|
health,
|
||||||
&mut target_buffs,
|
target_buffs,
|
||||||
|
stats,
|
||||||
&read_data,
|
&read_data,
|
||||||
&mut server_emitter,
|
&mut server_emitter,
|
||||||
);
|
);
|
||||||
@ -145,18 +134,19 @@ impl<'a> System<'a> for Sys {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
auras.set_event_emission(true);
|
|
||||||
buffs.set_event_emission(true);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[warn(clippy::pedantic)]
|
#[warn(clippy::pedantic)]
|
||||||
//#[warn(clippy::nursery)]
|
//#[warn(clippy::nursery)]
|
||||||
fn activate_aura(
|
fn activate_aura(
|
||||||
|
key: AuraKey,
|
||||||
aura: &Aura,
|
aura: &Aura,
|
||||||
|
applier: Uid,
|
||||||
target: EcsEntity,
|
target: EcsEntity,
|
||||||
health: &Health,
|
health: &Health,
|
||||||
target_buffs: &mut Buffs,
|
target_buffs: &Buffs,
|
||||||
|
stats: Option<&Stats>,
|
||||||
read_data: &ReadData,
|
read_data: &ReadData,
|
||||||
server_emitter: &mut Emitter<ServerEvent>,
|
server_emitter: &mut Emitter<ServerEvent>,
|
||||||
) {
|
) {
|
||||||
@ -237,12 +227,9 @@ fn activate_aura(
|
|||||||
let emit_buff = !target_buffs.buffs.iter().any(|(_, buff)| {
|
let emit_buff = !target_buffs.buffs.iter().any(|(_, buff)| {
|
||||||
buff.cat_ids
|
buff.cat_ids
|
||||||
.iter()
|
.iter()
|
||||||
.any(|cat_id| matches!(cat_id, BuffCategory::FromAura(_)))
|
.any(|cat_id| matches!(cat_id, BuffCategory::FromActiveAura(uid, aura_key) if *aura_key == key && *uid == applier))
|
||||||
&& buff.kind == kind
|
&& buff.kind == kind
|
||||||
&& buff.data.strength >= data.strength
|
&& buff.data.strength >= data.strength
|
||||||
&& buff.time.map_or(true, |dur| {
|
|
||||||
data.duration.map_or(false, |dur_2| dur >= dur_2)
|
|
||||||
})
|
|
||||||
});
|
});
|
||||||
if emit_buff {
|
if emit_buff {
|
||||||
server_emitter.emit(ServerEvent::Buff {
|
server_emitter.emit(ServerEvent::Buff {
|
||||||
@ -250,29 +237,13 @@ fn activate_aura(
|
|||||||
buff_change: BuffChange::Add(Buff::new(
|
buff_change: BuffChange::Add(Buff::new(
|
||||||
kind,
|
kind,
|
||||||
data,
|
data,
|
||||||
vec![category, BuffCategory::FromAura(true)],
|
vec![category, BuffCategory::FromActiveAura(applier, key)],
|
||||||
source,
|
source,
|
||||||
|
*read_data.time,
|
||||||
|
stats,
|
||||||
)),
|
)),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// Finds all buffs on target that are from an aura, are of
|
|
||||||
// the same buff kind, and are of at most the same strength.
|
|
||||||
// For any such buffs, marks it as recently applied.
|
|
||||||
for (_, buff) in target_buffs.buffs.iter_mut().filter(|(_, buff)| {
|
|
||||||
buff.cat_ids
|
|
||||||
.iter()
|
|
||||||
.any(|cat_id| matches!(cat_id, BuffCategory::FromAura(_)))
|
|
||||||
&& buff.kind == kind
|
|
||||||
&& buff.data.strength <= data.strength
|
|
||||||
}) {
|
|
||||||
if let Some(cat_id) = buff
|
|
||||||
.cat_ids
|
|
||||||
.iter_mut()
|
|
||||||
.find(|cat_id| matches!(cat_id, BuffCategory::FromAura(false)))
|
|
||||||
{
|
|
||||||
*cat_id = BuffCategory::FromAura(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
use common::{
|
use common::{
|
||||||
combat::DamageContributor,
|
combat::DamageContributor,
|
||||||
comp::{
|
comp::{
|
||||||
|
aura::Auras,
|
||||||
body::{object, Body},
|
body::{object, Body},
|
||||||
buff::{
|
buff::{
|
||||||
Buff, BuffCategory, BuffChange, BuffData, BuffEffect, BuffId, BuffKind, BuffSource,
|
Buff, BuffCategory, BuffChange, BuffData, BuffEffect, BuffId, BuffKind, BuffSource,
|
||||||
@ -9,23 +10,21 @@ use common::{
|
|||||||
fluid_dynamics::{Fluid, LiquidKind},
|
fluid_dynamics::{Fluid, LiquidKind},
|
||||||
item::MaterialStatManifest,
|
item::MaterialStatManifest,
|
||||||
Energy, Group, Health, HealthChange, Inventory, LightEmitter, ModifierKind, PhysicsState,
|
Energy, Group, Health, HealthChange, Inventory, LightEmitter, ModifierKind, PhysicsState,
|
||||||
Stats,
|
Pos, Stats,
|
||||||
},
|
},
|
||||||
event::{Emitter, EventBus, ServerEvent},
|
event::{Emitter, EventBus, ServerEvent},
|
||||||
resources::{DeltaTime, Time},
|
resources::{DeltaTime, Secs, Time},
|
||||||
terrain::SpriteKind,
|
terrain::SpriteKind,
|
||||||
uid::{Uid, UidAllocator},
|
uid::{Uid, UidAllocator},
|
||||||
Damage, DamageSource,
|
Damage, DamageSource,
|
||||||
};
|
};
|
||||||
use common_base::prof_span;
|
use common_base::prof_span;
|
||||||
use common_ecs::{Job, Origin, ParMode, Phase, System};
|
use common_ecs::{Job, Origin, ParMode, Phase, System};
|
||||||
use hashbrown::HashMap;
|
|
||||||
use rayon::iter::ParallelIterator;
|
use rayon::iter::ParallelIterator;
|
||||||
use specs::{
|
use specs::{
|
||||||
saveload::MarkerAllocator, shred::ResourceId, Entities, Entity, Join, ParJoin, Read,
|
saveload::MarkerAllocator, shred::ResourceId, Entities, Entity, Join, ParJoin, Read,
|
||||||
ReadExpect, ReadStorage, SystemData, World, WriteStorage,
|
ReadExpect, ReadStorage, SystemData, World, WriteStorage,
|
||||||
};
|
};
|
||||||
use std::time::Duration;
|
|
||||||
|
|
||||||
#[derive(SystemData)]
|
#[derive(SystemData)]
|
||||||
pub struct ReadData<'a> {
|
pub struct ReadData<'a> {
|
||||||
@ -40,6 +39,9 @@ pub struct ReadData<'a> {
|
|||||||
uid_allocator: Read<'a, UidAllocator>,
|
uid_allocator: Read<'a, UidAllocator>,
|
||||||
time: Read<'a, Time>,
|
time: Read<'a, Time>,
|
||||||
msm: ReadExpect<'a, MaterialStatManifest>,
|
msm: ReadExpect<'a, MaterialStatManifest>,
|
||||||
|
buffs: ReadStorage<'a, Buffs>,
|
||||||
|
auras: ReadStorage<'a, Auras>,
|
||||||
|
positions: ReadStorage<'a, Pos>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
@ -47,7 +49,6 @@ pub struct Sys;
|
|||||||
impl<'a> System<'a> for Sys {
|
impl<'a> System<'a> for Sys {
|
||||||
type SystemData = (
|
type SystemData = (
|
||||||
ReadData<'a>,
|
ReadData<'a>,
|
||||||
WriteStorage<'a, Buffs>,
|
|
||||||
WriteStorage<'a, Stats>,
|
WriteStorage<'a, Stats>,
|
||||||
WriteStorage<'a, Body>,
|
WriteStorage<'a, Body>,
|
||||||
WriteStorage<'a, LightEmitter>,
|
WriteStorage<'a, LightEmitter>,
|
||||||
@ -59,12 +60,11 @@ impl<'a> System<'a> for Sys {
|
|||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
job: &mut Job<Self>,
|
job: &mut Job<Self>,
|
||||||
(read_data, mut buffs, mut stats, mut bodies, mut light_emitters): Self::SystemData,
|
(read_data, mut stats, mut bodies, mut light_emitters): Self::SystemData,
|
||||||
) {
|
) {
|
||||||
let mut server_emitter = read_data.server_bus.emitter();
|
let mut server_emitter = read_data.server_bus.emitter();
|
||||||
let dt = read_data.dt.0;
|
let dt = read_data.dt.0;
|
||||||
// Set to false to avoid spamming server
|
// Set to false to avoid spamming server
|
||||||
buffs.set_event_emission(false);
|
|
||||||
stats.set_event_emission(false);
|
stats.set_event_emission(false);
|
||||||
|
|
||||||
// Put out underwater campfires. Logically belongs here since this system also
|
// Put out underwater campfires. Logically belongs here since this system also
|
||||||
@ -125,9 +125,9 @@ impl<'a> System<'a> for Sys {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (entity, mut buff_comp, mut stat, health, energy, physics_state) in (
|
for (entity, buff_comp, mut stat, health, energy, physics_state) in (
|
||||||
&read_data.entities,
|
&read_data.entities,
|
||||||
&mut buffs,
|
&read_data.buffs,
|
||||||
&mut stats,
|
&mut stats,
|
||||||
&read_data.healths,
|
&read_data.healths,
|
||||||
&read_data.energies,
|
&read_data.energies,
|
||||||
@ -146,9 +146,11 @@ impl<'a> System<'a> for Sys {
|
|||||||
entity,
|
entity,
|
||||||
buff_change: BuffChange::Add(Buff::new(
|
buff_change: BuffChange::Add(Buff::new(
|
||||||
BuffKind::Ensnared,
|
BuffKind::Ensnared,
|
||||||
BuffData::new(1.0, Some(Duration::from_secs_f32(1.0)), None),
|
BuffData::new(1.0, Some(Secs(1.0)), None),
|
||||||
Vec::new(),
|
Vec::new(),
|
||||||
BuffSource::World,
|
BuffSource::World,
|
||||||
|
*read_data.time,
|
||||||
|
Some(&stat),
|
||||||
)),
|
)),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -161,9 +163,11 @@ impl<'a> System<'a> for Sys {
|
|||||||
entity,
|
entity,
|
||||||
buff_change: BuffChange::Add(Buff::new(
|
buff_change: BuffChange::Add(Buff::new(
|
||||||
BuffKind::Bleeding,
|
BuffKind::Bleeding,
|
||||||
BuffData::new(1.0, Some(Duration::from_secs_f32(6.0)), None),
|
BuffData::new(1.0, Some(Secs(6.0)), None),
|
||||||
Vec::new(),
|
Vec::new(),
|
||||||
BuffSource::World,
|
BuffSource::World,
|
||||||
|
*read_data.time,
|
||||||
|
Some(&stat),
|
||||||
)),
|
)),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -176,9 +180,11 @@ impl<'a> System<'a> for Sys {
|
|||||||
entity,
|
entity,
|
||||||
buff_change: BuffChange::Add(Buff::new(
|
buff_change: BuffChange::Add(Buff::new(
|
||||||
BuffKind::Bleeding,
|
BuffKind::Bleeding,
|
||||||
BuffData::new(15.0, Some(Duration::from_secs_f32(0.1)), None),
|
BuffData::new(15.0, Some(Secs(0.1)), None),
|
||||||
Vec::new(),
|
Vec::new(),
|
||||||
BuffSource::World,
|
BuffSource::World,
|
||||||
|
*read_data.time,
|
||||||
|
Some(&stat),
|
||||||
)),
|
)),
|
||||||
});
|
});
|
||||||
// When standing on IceSpike also apply Frozen
|
// When standing on IceSpike also apply Frozen
|
||||||
@ -186,9 +192,11 @@ impl<'a> System<'a> for Sys {
|
|||||||
entity,
|
entity,
|
||||||
buff_change: BuffChange::Add(Buff::new(
|
buff_change: BuffChange::Add(Buff::new(
|
||||||
BuffKind::Frozen,
|
BuffKind::Frozen,
|
||||||
BuffData::new(0.2, Some(Duration::from_secs_f32(1.0)), None),
|
BuffData::new(0.2, Some(Secs(1.0)), None),
|
||||||
Vec::new(),
|
Vec::new(),
|
||||||
BuffSource::World,
|
BuffSource::World,
|
||||||
|
*read_data.time,
|
||||||
|
Some(&stat),
|
||||||
)),
|
)),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -207,6 +215,8 @@ impl<'a> System<'a> for Sys {
|
|||||||
BuffData::new(20.0, None, None),
|
BuffData::new(20.0, None, None),
|
||||||
vec![BuffCategory::Natural],
|
vec![BuffCategory::Natural],
|
||||||
BuffSource::World,
|
BuffSource::World,
|
||||||
|
*read_data.time,
|
||||||
|
Some(&stat),
|
||||||
)),
|
)),
|
||||||
});
|
});
|
||||||
} else if matches!(
|
} else if matches!(
|
||||||
@ -225,31 +235,77 @@ impl<'a> System<'a> for Sys {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let (buff_comp_kinds, buff_comp_buffs): (
|
|
||||||
&HashMap<BuffKind, Vec<BuffId>>,
|
|
||||||
&mut HashMap<BuffId, Buff>,
|
|
||||||
) = buff_comp.parts();
|
|
||||||
let mut expired_buffs = Vec::<BuffId>::new();
|
let mut expired_buffs = Vec::<BuffId>::new();
|
||||||
|
|
||||||
// For each buff kind present on entity, if the buff kind queues, only ticks
|
// Replace buffs from an active aura with a normal buff when out of range of the
|
||||||
// duration of strongest buff of that kind, else it ticks durations of all buffs
|
// aura
|
||||||
// of that kind. Any buffs whose durations expire are marked expired.
|
buff_comp
|
||||||
for (kind, ids) in buff_comp_kinds.iter() {
|
.buffs
|
||||||
if kind.queues() {
|
.iter()
|
||||||
if let Some((Some(buff), id)) =
|
.filter_map(|(id, buff)| {
|
||||||
ids.first().map(|id| (buff_comp_buffs.get_mut(id), id))
|
if let Some((uid, aura_key)) = buff.cat_ids.iter().find_map(|cat_id| {
|
||||||
|
if let BuffCategory::FromActiveAura(uid, aura_key) = cat_id {
|
||||||
|
Some((uid, aura_key))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}) {
|
||||||
|
Some((id, buff, uid, aura_key))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.for_each(|(buff_id, buff, uid, aura_key)| {
|
||||||
|
let replace = if let Some(aura_entity) = read_data
|
||||||
|
.uid_allocator
|
||||||
|
.retrieve_entity_internal((*uid).into())
|
||||||
{
|
{
|
||||||
tick_buff(*id, buff, dt, |id| expired_buffs.push(id));
|
if let Some(aura) = read_data
|
||||||
|
.auras
|
||||||
|
.get(aura_entity)
|
||||||
|
.and_then(|auras| auras.auras.get(*aura_key))
|
||||||
|
{
|
||||||
|
if let (Some(pos), Some(aura_pos)) = (
|
||||||
|
read_data.positions.get(entity),
|
||||||
|
read_data.positions.get(aura_entity),
|
||||||
|
) {
|
||||||
|
pos.0.distance_squared(aura_pos.0) > aura.radius.powi(2)
|
||||||
|
} else {
|
||||||
|
true
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (id, buff) in buff_comp_buffs
|
true
|
||||||
.iter_mut()
|
|
||||||
.filter(|(i, _)| ids.iter().any(|id| id == *i))
|
|
||||||
{
|
|
||||||
tick_buff(*id, buff, dt, |id| expired_buffs.push(id));
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
};
|
||||||
|
if replace {
|
||||||
|
expired_buffs.push(*buff_id);
|
||||||
|
server_emitter.emit(ServerEvent::Buff {
|
||||||
|
entity,
|
||||||
|
buff_change: BuffChange::Add(Buff::new(
|
||||||
|
buff.kind,
|
||||||
|
buff.data,
|
||||||
|
buff.cat_ids
|
||||||
|
.iter()
|
||||||
|
.copied()
|
||||||
|
.filter(|cat_id| {
|
||||||
|
!matches!(cat_id, BuffCategory::FromActiveAura(..))
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>(),
|
||||||
|
buff.source,
|
||||||
|
*read_data.time,
|
||||||
|
Some(&stat),
|
||||||
|
)),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
buff_comp.buffs.iter().for_each(|(id, buff)| {
|
||||||
|
if buff.end_time.map_or(false, |end| end.0 < read_data.time.0) {
|
||||||
|
expired_buffs.push(*id)
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
let damage_reduction = Damage::compute_damage_reduction(
|
let damage_reduction = Damage::compute_damage_reduction(
|
||||||
None,
|
None,
|
||||||
@ -269,14 +325,13 @@ impl<'a> System<'a> for Sys {
|
|||||||
stat.reset_temp_modifiers();
|
stat.reset_temp_modifiers();
|
||||||
|
|
||||||
// Iterator over the lists of buffs by kind
|
// Iterator over the lists of buffs by kind
|
||||||
let buff_comp = &mut *buff_comp;
|
|
||||||
let mut buff_kinds = buff_comp
|
let mut buff_kinds = buff_comp
|
||||||
.kinds
|
.kinds
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(kind, ids)| (*kind, ids.clone()))
|
.map(|(kind, ids)| (*kind, ids.clone()))
|
||||||
.collect::<Vec<(BuffKind, Vec<BuffId>)>>();
|
.collect::<Vec<(BuffKind, (Vec<BuffId>, Time))>>();
|
||||||
buff_kinds.sort_by_key(|(kind, _)| !kind.affects_subsequent_buffs());
|
buff_kinds.sort_by_key(|(kind, _)| !kind.affects_subsequent_buffs());
|
||||||
for (buff_kind, buff_ids) in buff_kinds.into_iter() {
|
for (buff_kind, (buff_ids, kind_start_time)) in buff_kinds.into_iter() {
|
||||||
let mut active_buff_ids = Vec::new();
|
let mut active_buff_ids = Vec::new();
|
||||||
if buff_kind.stacks() {
|
if buff_kind.stacks() {
|
||||||
// Process all the buffs of this kind
|
// Process all the buffs of this kind
|
||||||
@ -286,9 +341,9 @@ impl<'a> System<'a> for Sys {
|
|||||||
active_buff_ids.push(buff_ids[0]);
|
active_buff_ids.push(buff_ids[0]);
|
||||||
}
|
}
|
||||||
for buff_id in active_buff_ids.into_iter() {
|
for buff_id in active_buff_ids.into_iter() {
|
||||||
if let Some(buff) = buff_comp.buffs.get_mut(&buff_id) {
|
if let Some(buff) = buff_comp.buffs.get(&buff_id) {
|
||||||
// Skip the effect of buffs whose start delay hasn't expired.
|
// Skip the effect of buffs whose start delay hasn't expired.
|
||||||
if buff.delay.is_some() {
|
if buff.start_time.0 > read_data.time.0 {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// Get buff owner?
|
// Get buff owner?
|
||||||
@ -299,11 +354,12 @@ impl<'a> System<'a> for Sys {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Now, execute the buff, based on it's delta
|
// Now, execute the buff, based on it's delta
|
||||||
for effect in &mut buff.effects {
|
for effect in &buff.effects {
|
||||||
execute_effect(
|
execute_effect(
|
||||||
effect,
|
effect,
|
||||||
buff.kind,
|
buff.kind,
|
||||||
buff.time,
|
buff.start_time,
|
||||||
|
kind_start_time,
|
||||||
&read_data,
|
&read_data,
|
||||||
&mut stat,
|
&mut stat,
|
||||||
health,
|
health,
|
||||||
@ -312,6 +368,8 @@ impl<'a> System<'a> for Sys {
|
|||||||
buff_owner,
|
buff_owner,
|
||||||
&mut server_emitter,
|
&mut server_emitter,
|
||||||
dt,
|
dt,
|
||||||
|
*read_data.time,
|
||||||
|
expired_buffs.contains(&buff_id),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -339,15 +397,15 @@ impl<'a> System<'a> for Sys {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Turned back to true
|
// Turned back to true
|
||||||
buffs.set_event_emission(true);
|
|
||||||
stats.set_event_emission(true);
|
stats.set_event_emission(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn execute_effect(
|
fn execute_effect(
|
||||||
effect: &mut BuffEffect,
|
effect: &BuffEffect,
|
||||||
buff_kind: BuffKind,
|
buff_kind: BuffKind,
|
||||||
buff_time: Option<std::time::Duration>,
|
buff_start_time: Time,
|
||||||
|
buff_kind_start_time: Time,
|
||||||
read_data: &ReadData,
|
read_data: &ReadData,
|
||||||
stat: &mut Stats,
|
stat: &mut Stats,
|
||||||
health: &Health,
|
health: &Health,
|
||||||
@ -356,28 +414,37 @@ fn execute_effect(
|
|||||||
buff_owner: Option<Uid>,
|
buff_owner: Option<Uid>,
|
||||||
server_emitter: &mut Emitter<ServerEvent>,
|
server_emitter: &mut Emitter<ServerEvent>,
|
||||||
dt: f32,
|
dt: f32,
|
||||||
|
time: Time,
|
||||||
|
buff_will_expire: bool,
|
||||||
) {
|
) {
|
||||||
match effect {
|
match effect {
|
||||||
BuffEffect::HealthChangeOverTime {
|
BuffEffect::HealthChangeOverTime {
|
||||||
rate,
|
rate,
|
||||||
accumulated,
|
|
||||||
kind,
|
kind,
|
||||||
instance,
|
instance,
|
||||||
} => {
|
} => {
|
||||||
*accumulated += *rate * dt;
|
// Apply health change if buff wasn't just added, only once per second or when
|
||||||
// Apply health change only once per second, per health, or
|
// buff is about to be removed
|
||||||
// when a buff is removed
|
let time_passed = (time.0 - buff_start_time.0) as f32;
|
||||||
if accumulated.abs() > rate.abs().min(1.0)
|
let one_second = (time_passed % 1.0) < dt;
|
||||||
|| buff_time.map_or(false, |dur| dur == Duration::default())
|
if time_passed > dt && (one_second || buff_will_expire) {
|
||||||
{
|
let amount = if one_second {
|
||||||
let (cause, by) = if *accumulated != 0.0 {
|
// If buff not expiring this tick, then 1 second has passed
|
||||||
|
*rate
|
||||||
|
} else {
|
||||||
|
// If buff expiring this tick, only a fraction of the second has passed since
|
||||||
|
// last tick
|
||||||
|
((time.0 - buff_start_time.0) % 1.0) as f32 * rate
|
||||||
|
};
|
||||||
|
|
||||||
|
let (cause, by) = if amount != 0.0 {
|
||||||
(Some(DamageSource::Buff(buff_kind)), buff_owner)
|
(Some(DamageSource::Buff(buff_kind)), buff_owner)
|
||||||
} else {
|
} else {
|
||||||
(None, None)
|
(None, None)
|
||||||
};
|
};
|
||||||
let mut amount = match *kind {
|
let amount = match *kind {
|
||||||
ModifierKind::Additive => *accumulated,
|
ModifierKind::Additive => amount,
|
||||||
ModifierKind::Fractional => health.maximum() * *accumulated,
|
ModifierKind::Fractional => health.maximum() * amount,
|
||||||
};
|
};
|
||||||
let damage_contributor = by.and_then(|uid| {
|
let damage_contributor = by.and_then(|uid| {
|
||||||
read_data
|
read_data
|
||||||
@ -387,9 +454,6 @@ fn execute_effect(
|
|||||||
DamageContributor::new(uid, read_data.groups.get(entity).cloned())
|
DamageContributor::new(uid, read_data.groups.get(entity).cloned())
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
if amount > 0.0 && matches!(buff_kind, BuffKind::Potion) {
|
|
||||||
amount *= stat.heal_multiplier;
|
|
||||||
}
|
|
||||||
server_emitter.emit(ServerEvent::HealthChange {
|
server_emitter.emit(ServerEvent::HealthChange {
|
||||||
entity,
|
entity,
|
||||||
change: HealthChange {
|
change: HealthChange {
|
||||||
@ -401,29 +465,31 @@ fn execute_effect(
|
|||||||
instance: *instance,
|
instance: *instance,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
*accumulated = 0.0;
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
BuffEffect::EnergyChangeOverTime {
|
BuffEffect::EnergyChangeOverTime { rate, kind } => {
|
||||||
rate,
|
// Apply energy change if buff wasn't just added, only once per second or when
|
||||||
accumulated,
|
// buff is about to be removed
|
||||||
kind,
|
let time_passed = (time.0 - buff_start_time.0) as f32;
|
||||||
} => {
|
let one_second = (time_passed % 1.0) < dt;
|
||||||
*accumulated += *rate * dt;
|
if time_passed > dt && (one_second || buff_will_expire) {
|
||||||
// Apply energy change only once per second, per energy, or
|
let amount = if one_second {
|
||||||
// when a buff is removed
|
// If buff not expiring this tick, then 1 second has passed
|
||||||
if accumulated.abs() > rate.abs().min(10.0)
|
*rate
|
||||||
|| buff_time.map_or(false, |dur| dur == Duration::default())
|
} else {
|
||||||
{
|
// If buff expiring this tick, only a fraction of the second has passed since
|
||||||
|
// last tick
|
||||||
|
((time.0 - buff_start_time.0) % 1.0) as f32 * rate
|
||||||
|
};
|
||||||
|
|
||||||
let amount = match *kind {
|
let amount = match *kind {
|
||||||
ModifierKind::Additive => *accumulated,
|
ModifierKind::Additive => amount,
|
||||||
ModifierKind::Fractional => energy.maximum() * *accumulated,
|
ModifierKind::Fractional => energy.maximum() * amount,
|
||||||
};
|
};
|
||||||
server_emitter.emit(ServerEvent::EnergyChange {
|
server_emitter.emit(ServerEvent::EnergyChange {
|
||||||
entity,
|
entity,
|
||||||
change: amount,
|
change: amount,
|
||||||
});
|
});
|
||||||
*accumulated = 0.0;
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
BuffEffect::MaxHealthModifier { value, kind } => match kind {
|
BuffEffect::MaxHealthModifier { value, kind } => match kind {
|
||||||
@ -450,35 +516,23 @@ fn execute_effect(
|
|||||||
rate,
|
rate,
|
||||||
kind,
|
kind,
|
||||||
target_fraction,
|
target_fraction,
|
||||||
achieved_fraction,
|
|
||||||
} => {
|
} => {
|
||||||
// Current fraction uses information from last tick, which is
|
let potential_amount = (time.0 - buff_kind_start_time.0) as f32 * rate;
|
||||||
// necessary as buffs from this tick are not guaranteed to have
|
|
||||||
// finished applying
|
|
||||||
let current_fraction = health.maximum() / health.base_max();
|
|
||||||
|
|
||||||
// If achieved_fraction not initialized, initialize it to health
|
|
||||||
// fraction
|
|
||||||
if achieved_fraction.is_none() {
|
|
||||||
*achieved_fraction = Some(current_fraction)
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(achieved_fraction) = achieved_fraction {
|
|
||||||
// Percentage change that should be applied to max_health
|
// Percentage change that should be applied to max_health
|
||||||
let health_tick = match kind {
|
let potential_fraction = 1.0
|
||||||
|
+ match kind {
|
||||||
ModifierKind::Additive => {
|
ModifierKind::Additive => {
|
||||||
// `rate * dt` is amount of health, dividing by base max
|
// `rate * dt` is amount of health, dividing by base max
|
||||||
// creates fraction
|
// creates fraction
|
||||||
*rate * dt / health.base_max()
|
potential_amount / health.base_max()
|
||||||
},
|
},
|
||||||
ModifierKind::Fractional => {
|
ModifierKind::Fractional => {
|
||||||
// `rate * dt` is the fraction
|
// `rate * dt` is the fraction
|
||||||
*rate * dt
|
potential_amount
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
let potential_fraction = *achieved_fraction + health_tick;
|
|
||||||
|
|
||||||
// Potential progress towards target fraction, if
|
// Potential progress towards target fraction, if
|
||||||
// target_fraction ~ 1.0 then set progress to 1.0 to avoid
|
// target_fraction ~ 1.0 then set progress to 1.0 to avoid
|
||||||
// divide by zero
|
// divide by zero
|
||||||
@ -490,20 +544,18 @@ fn execute_effect(
|
|||||||
|
|
||||||
// Change achieved_fraction depending on what other buffs have
|
// Change achieved_fraction depending on what other buffs have
|
||||||
// occurred
|
// occurred
|
||||||
if progress > 1.0 {
|
let achieved_fraction = if progress > 1.0 {
|
||||||
// If potential fraction already beyond target fraction,
|
// If potential fraction already beyond target fraction,
|
||||||
// simply multiply max_health_modifier by the target
|
// simply multiply max_health_modifier by the target
|
||||||
// fraction, and set achieved fraction to target_fraction
|
// fraction, and set achieved fraction to target_fraction
|
||||||
*achieved_fraction = *target_fraction;
|
*target_fraction
|
||||||
} else {
|
} else {
|
||||||
// Else have not achieved target yet, update
|
// Else have not achieved target yet, use potential_fraction
|
||||||
// achieved_fraction
|
potential_fraction
|
||||||
*achieved_fraction = potential_fraction;
|
};
|
||||||
}
|
|
||||||
|
|
||||||
// Apply achieved_fraction to max_health_modifier
|
// Apply achieved_fraction to max_health_modifier
|
||||||
stat.max_health_modifiers.mult_mod *= *achieved_fraction;
|
stat.max_health_modifiers.mult_mod *= achieved_fraction;
|
||||||
}
|
|
||||||
},
|
},
|
||||||
BuffEffect::MovementSpeed(speed) => {
|
BuffEffect::MovementSpeed(speed) => {
|
||||||
stat.move_speed_modifier *= *speed;
|
stat.move_speed_modifier *= *speed;
|
||||||
@ -518,35 +570,8 @@ fn execute_effect(
|
|||||||
BuffEffect::PoiseReduction(pr) => {
|
BuffEffect::PoiseReduction(pr) => {
|
||||||
stat.poise_reduction = stat.poise_reduction.max(*pr).min(1.0);
|
stat.poise_reduction = stat.poise_reduction.max(*pr).min(1.0);
|
||||||
},
|
},
|
||||||
BuffEffect::HealReduction { rate } => {
|
BuffEffect::HealReduction(red) => {
|
||||||
stat.heal_multiplier *= 1.0 - *rate;
|
stat.heal_multiplier *= 1.0 - *red;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tick_buff(id: u64, buff: &mut Buff, dt: f32, mut expire_buff: impl FnMut(u64)) {
|
|
||||||
// If a buff is recently applied from an aura, do not tick duration
|
|
||||||
if buff
|
|
||||||
.cat_ids
|
|
||||||
.iter()
|
|
||||||
.any(|cat_id| matches!(cat_id, BuffCategory::FromAura(true)))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if let Some(remaining_delay) = buff.delay {
|
|
||||||
buff.delay = remaining_delay.checked_sub(Duration::from_secs_f32(dt));
|
|
||||||
}
|
|
||||||
if let Some(remaining_time) = &mut buff.time {
|
|
||||||
if let Some(new_duration) = remaining_time.checked_sub(Duration::from_secs_f32(dt)) {
|
|
||||||
// The buff still continues.
|
|
||||||
*remaining_time = new_duration;
|
|
||||||
} else {
|
|
||||||
// checked_sub returns None when remaining time
|
|
||||||
// went below 0, so set to 0
|
|
||||||
*remaining_time = Duration::default();
|
|
||||||
// The buff has expired.
|
|
||||||
// Remove it.
|
|
||||||
expire_buff(id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -207,6 +207,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
&join_struct,
|
&join_struct,
|
||||||
&read_data.lazy_update,
|
&read_data.lazy_update,
|
||||||
&read_data.dt,
|
&read_data.dt,
|
||||||
|
&read_data.time,
|
||||||
&read_data.msm,
|
&read_data.msm,
|
||||||
&read_data.ability_map,
|
&read_data.ability_map,
|
||||||
);
|
);
|
||||||
@ -226,6 +227,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
&join_struct,
|
&join_struct,
|
||||||
&read_data.lazy_update,
|
&read_data.lazy_update,
|
||||||
&read_data.dt,
|
&read_data.dt,
|
||||||
|
&read_data.time,
|
||||||
&read_data.msm,
|
&read_data.msm,
|
||||||
&read_data.ability_map,
|
&read_data.ability_map,
|
||||||
);
|
);
|
||||||
|
@ -558,8 +558,7 @@ impl<'a> AgentData<'a> {
|
|||||||
Effect::Buff(BuffEffect { kind, data, .. })
|
Effect::Buff(BuffEffect { kind, data, .. })
|
||||||
if matches!(kind, Regeneration | Saturation | Potion) =>
|
if matches!(kind, Regeneration | Saturation | Potion) =>
|
||||||
{
|
{
|
||||||
value += data.strength
|
value += data.strength * data.duration.map_or(0.0, |d| d.0 as f32);
|
||||||
* data.duration.map_or(0.0, |d| d.as_secs() as f32);
|
|
||||||
},
|
},
|
||||||
Effect::Buff(BuffEffect { kind, .. })
|
Effect::Buff(BuffEffect { kind, .. })
|
||||||
if matches!(kind, PotionSickness) =>
|
if matches!(kind, PotionSickness) =>
|
||||||
|
@ -203,7 +203,7 @@ impl Client {
|
|||||||
| ServerGeneral::ChatMsg(_)
|
| ServerGeneral::ChatMsg(_)
|
||||||
| ServerGeneral::ChatMode(_)
|
| ServerGeneral::ChatMode(_)
|
||||||
| ServerGeneral::SetPlayerEntity(_)
|
| ServerGeneral::SetPlayerEntity(_)
|
||||||
| ServerGeneral::TimeOfDay(_, _)
|
| ServerGeneral::TimeOfDay(_, _, _)
|
||||||
| ServerGeneral::EntitySync(_)
|
| ServerGeneral::EntitySync(_)
|
||||||
| ServerGeneral::CompSync(_, _)
|
| ServerGeneral::CompSync(_, _)
|
||||||
| ServerGeneral::CreateEntity(_)
|
| ServerGeneral::CreateEntity(_)
|
||||||
|
@ -42,7 +42,7 @@ use common::{
|
|||||||
npc::{self, get_npc_name},
|
npc::{self, get_npc_name},
|
||||||
outcome::Outcome,
|
outcome::Outcome,
|
||||||
parse_cmd_args,
|
parse_cmd_args,
|
||||||
resources::{BattleMode, PlayerPhysicsSettings, Time, TimeOfDay},
|
resources::{BattleMode, PlayerPhysicsSettings, Secs, Time, TimeOfDay},
|
||||||
terrain::{Block, BlockKind, CoordinateConversions, SpriteKind, TerrainChunkSize},
|
terrain::{Block, BlockKind, CoordinateConversions, SpriteKind, TerrainChunkSize},
|
||||||
uid::{Uid, UidAllocator},
|
uid::{Uid, UidAllocator},
|
||||||
vol::ReadVol,
|
vol::ReadVol,
|
||||||
@ -53,7 +53,7 @@ use common_net::{
|
|||||||
sync::WorldSyncExt,
|
sync::WorldSyncExt,
|
||||||
};
|
};
|
||||||
use common_state::{BuildAreaError, BuildAreas};
|
use common_state::{BuildAreaError, BuildAreas};
|
||||||
use core::{cmp::Ordering, convert::TryFrom, time::Duration};
|
use core::{cmp::Ordering, convert::TryFrom};
|
||||||
use hashbrown::{HashMap, HashSet};
|
use hashbrown::{HashMap, HashSet};
|
||||||
use humantime::Duration as HumanDuration;
|
use humantime::Duration as HumanDuration;
|
||||||
use rand::{thread_rng, Rng};
|
use rand::{thread_rng, Rng};
|
||||||
@ -1045,6 +1045,7 @@ fn handle_time(
|
|||||||
};
|
};
|
||||||
|
|
||||||
server.state.mut_resource::<TimeOfDay>().0 = new_time;
|
server.state.mut_resource::<TimeOfDay>().0 = new_time;
|
||||||
|
let time = server.state.ecs().read_resource::<Time>();
|
||||||
|
|
||||||
// Update all clients with the new TimeOfDay (without this they would have to
|
// Update all clients with the new TimeOfDay (without this they would have to
|
||||||
// wait for the next 100th tick to receive the update).
|
// wait for the next 100th tick to receive the update).
|
||||||
@ -1056,6 +1057,7 @@ fn handle_time(
|
|||||||
client.prepare(ServerGeneral::TimeOfDay(
|
client.prepare(ServerGeneral::TimeOfDay(
|
||||||
TimeOfDay(new_time),
|
TimeOfDay(new_time),
|
||||||
(*calendar).clone(),
|
(*calendar).clone(),
|
||||||
|
*time,
|
||||||
))
|
))
|
||||||
});
|
});
|
||||||
let _ = client.send_prepared(&msg);
|
let _ = client.send_prepared(&msg);
|
||||||
@ -1462,6 +1464,7 @@ fn handle_spawn_campfire(
|
|||||||
_action: &ServerChatCommand,
|
_action: &ServerChatCommand,
|
||||||
) -> CmdResult<()> {
|
) -> CmdResult<()> {
|
||||||
let pos = position(server, target, "target")?;
|
let pos = position(server, target, "target")?;
|
||||||
|
let time = server.state.get_time();
|
||||||
server
|
server
|
||||||
.state
|
.state
|
||||||
.create_object(pos, comp::object::Body::CampfireLit)
|
.create_object(pos, comp::object::Body::CampfireLit)
|
||||||
@ -1476,24 +1479,26 @@ fn handle_spawn_campfire(
|
|||||||
Aura::new(
|
Aura::new(
|
||||||
AuraKind::Buff {
|
AuraKind::Buff {
|
||||||
kind: BuffKind::CampfireHeal,
|
kind: BuffKind::CampfireHeal,
|
||||||
data: BuffData::new(0.02, Some(Duration::from_secs(1)), None),
|
data: BuffData::new(0.02, Some(Secs(1.0)), None),
|
||||||
category: BuffCategory::Natural,
|
category: BuffCategory::Natural,
|
||||||
source: BuffSource::World,
|
source: BuffSource::World,
|
||||||
},
|
},
|
||||||
5.0,
|
5.0,
|
||||||
None,
|
None,
|
||||||
AuraTarget::All,
|
AuraTarget::All,
|
||||||
|
Time(time),
|
||||||
),
|
),
|
||||||
Aura::new(
|
Aura::new(
|
||||||
AuraKind::Buff {
|
AuraKind::Buff {
|
||||||
kind: BuffKind::Burning,
|
kind: BuffKind::Burning,
|
||||||
data: BuffData::new(2.0, Some(Duration::from_secs(10)), None),
|
data: BuffData::new(2.0, Some(Secs(10.0)), None),
|
||||||
category: BuffCategory::Natural,
|
category: BuffCategory::Natural,
|
||||||
source: BuffSource::World,
|
source: BuffSource::World,
|
||||||
},
|
},
|
||||||
0.7,
|
0.7,
|
||||||
None,
|
None,
|
||||||
AuraTarget::All,
|
AuraTarget::All,
|
||||||
|
Time(time),
|
||||||
),
|
),
|
||||||
]))
|
]))
|
||||||
.build();
|
.build();
|
||||||
@ -3516,8 +3521,8 @@ fn handle_buff(
|
|||||||
) -> CmdResult<()> {
|
) -> CmdResult<()> {
|
||||||
if let (Some(buff), strength, duration) = parse_cmd_args!(args, String, f32, f64) {
|
if let (Some(buff), strength, duration) = parse_cmd_args!(args, String, f32, f64) {
|
||||||
let strength = strength.unwrap_or(0.01);
|
let strength = strength.unwrap_or(0.01);
|
||||||
let duration = Duration::from_secs_f64(duration.unwrap_or(1.0));
|
let duration = duration.unwrap_or(1.0);
|
||||||
let buffdata = BuffData::new(strength, Some(duration), None);
|
let buffdata = BuffData::new(strength, Some(Secs(duration)), None);
|
||||||
if buff != "all" {
|
if buff != "all" {
|
||||||
cast_buff(&buff, buffdata, server, target)
|
cast_buff(&buff, buffdata, server, target)
|
||||||
} else {
|
} else {
|
||||||
@ -3535,8 +3540,20 @@ fn cast_buff(kind: &str, data: BuffData, server: &mut Server, target: EcsEntity)
|
|||||||
if let Some(buffkind) = parse_buffkind(kind) {
|
if let Some(buffkind) = parse_buffkind(kind) {
|
||||||
let ecs = &server.state.ecs();
|
let ecs = &server.state.ecs();
|
||||||
let mut buffs_all = ecs.write_storage::<comp::Buffs>();
|
let mut buffs_all = ecs.write_storage::<comp::Buffs>();
|
||||||
|
let stats = ecs.read_storage::<comp::Stats>();
|
||||||
|
let time = ecs.read_resource::<Time>();
|
||||||
if let Some(mut buffs) = buffs_all.get_mut(target) {
|
if let Some(mut buffs) = buffs_all.get_mut(target) {
|
||||||
buffs.insert(Buff::new(buffkind, data, vec![], BuffSource::Command));
|
buffs.insert(
|
||||||
|
Buff::new(
|
||||||
|
buffkind,
|
||||||
|
data,
|
||||||
|
vec![],
|
||||||
|
BuffSource::Command,
|
||||||
|
*time,
|
||||||
|
stats.get(target),
|
||||||
|
),
|
||||||
|
*time,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
|
@ -14,6 +14,7 @@ use common::{
|
|||||||
event::{EventBus, UpdateCharacterMetadata},
|
event::{EventBus, UpdateCharacterMetadata},
|
||||||
lottery::LootSpec,
|
lottery::LootSpec,
|
||||||
outcome::Outcome,
|
outcome::Outcome,
|
||||||
|
resources::{Secs, Time},
|
||||||
rtsim::RtSimEntity,
|
rtsim::RtSimEntity,
|
||||||
uid::Uid,
|
uid::Uid,
|
||||||
util::Dir,
|
util::Dir,
|
||||||
@ -21,7 +22,6 @@ use common::{
|
|||||||
};
|
};
|
||||||
use common_net::{msg::ServerGeneral, sync::WorldSyncExt};
|
use common_net::{msg::ServerGeneral, sync::WorldSyncExt};
|
||||||
use specs::{Builder, Entity as EcsEntity, WorldExt};
|
use specs::{Builder, Entity as EcsEntity, WorldExt};
|
||||||
use std::time::Duration;
|
|
||||||
use vek::{Rgb, Vec3};
|
use vek::{Rgb, Vec3};
|
||||||
|
|
||||||
use super::group_manip::update_map_markers;
|
use super::group_manip::update_map_markers;
|
||||||
@ -270,6 +270,7 @@ pub fn handle_beam(server: &mut Server, properties: beam::Properties, pos: Pos,
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_create_waypoint(server: &mut Server, pos: Vec3<f32>) {
|
pub fn handle_create_waypoint(server: &mut Server, pos: Vec3<f32>) {
|
||||||
|
let time = server.state.get_time();
|
||||||
server
|
server
|
||||||
.state
|
.state
|
||||||
.create_object(Pos(pos), comp::object::Body::CampfireLit)
|
.create_object(Pos(pos), comp::object::Body::CampfireLit)
|
||||||
@ -285,24 +286,26 @@ pub fn handle_create_waypoint(server: &mut Server, pos: Vec3<f32>) {
|
|||||||
Aura::new(
|
Aura::new(
|
||||||
AuraKind::Buff {
|
AuraKind::Buff {
|
||||||
kind: BuffKind::CampfireHeal,
|
kind: BuffKind::CampfireHeal,
|
||||||
data: BuffData::new(0.02, Some(Duration::from_secs(1)), None),
|
data: BuffData::new(0.02, Some(Secs(1.0)), None),
|
||||||
category: BuffCategory::Natural,
|
category: BuffCategory::Natural,
|
||||||
source: BuffSource::World,
|
source: BuffSource::World,
|
||||||
},
|
},
|
||||||
5.0,
|
5.0,
|
||||||
None,
|
None,
|
||||||
AuraTarget::All,
|
AuraTarget::All,
|
||||||
|
Time(time),
|
||||||
),
|
),
|
||||||
Aura::new(
|
Aura::new(
|
||||||
AuraKind::Buff {
|
AuraKind::Buff {
|
||||||
kind: BuffKind::Burning,
|
kind: BuffKind::Burning,
|
||||||
data: BuffData::new(2.0, Some(Duration::from_secs(10)), None),
|
data: BuffData::new(2.0, Some(Secs(10.0)), None),
|
||||||
category: BuffCategory::Natural,
|
category: BuffCategory::Natural,
|
||||||
source: BuffSource::World,
|
source: BuffSource::World,
|
||||||
},
|
},
|
||||||
0.7,
|
0.7,
|
||||||
None,
|
None,
|
||||||
AuraTarget::All,
|
AuraTarget::All,
|
||||||
|
Time(time),
|
||||||
),
|
),
|
||||||
]))
|
]))
|
||||||
.build();
|
.build();
|
||||||
|
@ -25,7 +25,7 @@ use common::{
|
|||||||
},
|
},
|
||||||
event::{EventBus, ServerEvent},
|
event::{EventBus, ServerEvent},
|
||||||
outcome::{HealthChangeInfo, Outcome},
|
outcome::{HealthChangeInfo, Outcome},
|
||||||
resources::Time,
|
resources::{Secs, Time},
|
||||||
rtsim::RtSimEntity,
|
rtsim::RtSimEntity,
|
||||||
states::utils::{AbilityInfo, StageSection},
|
states::utils::{AbilityInfo, StageSection},
|
||||||
terrain::{Block, BlockKind, TerrainGrid},
|
terrain::{Block, BlockKind, TerrainGrid},
|
||||||
@ -1091,6 +1091,7 @@ pub fn handle_buff(server: &mut Server, entity: EcsEntity, buff_change: buff::Bu
|
|||||||
let ecs = &server.state.ecs();
|
let ecs = &server.state.ecs();
|
||||||
let mut buffs_all = ecs.write_storage::<comp::Buffs>();
|
let mut buffs_all = ecs.write_storage::<comp::Buffs>();
|
||||||
let bodies = ecs.read_storage::<Body>();
|
let bodies = ecs.read_storage::<Body>();
|
||||||
|
let time = ecs.read_resource::<Time>();
|
||||||
if let Some(mut buffs) = buffs_all.get_mut(entity) {
|
if let Some(mut buffs) = buffs_all.get_mut(entity) {
|
||||||
use buff::BuffChange;
|
use buff::BuffChange;
|
||||||
match buff_change {
|
match buff_change {
|
||||||
@ -1103,7 +1104,7 @@ pub fn handle_buff(server: &mut Server, entity: EcsEntity, buff_change: buff::Bu
|
|||||||
.get(entity)
|
.get(entity)
|
||||||
.map_or(true, |h| !h.is_dead)
|
.map_or(true, |h| !h.is_dead)
|
||||||
{
|
{
|
||||||
buffs.insert(new_buff);
|
buffs.insert(new_buff, *time);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
BuffChange::RemoveById(ids) => {
|
BuffChange::RemoveById(ids) => {
|
||||||
@ -1307,20 +1308,24 @@ pub fn handle_parry_hook(server: &Server, defender: EcsEntity, attacker: Option<
|
|||||||
let duration = char_state
|
let duration = char_state
|
||||||
.durations()
|
.durations()
|
||||||
.and_then(|durs| durs.recover)
|
.and_then(|durs| durs.recover)
|
||||||
.map_or(0.5, |dur| dur.as_secs_f32())
|
.map_or(0.5, |dur| dur.as_secs_f64())
|
||||||
.max(0.5)
|
.max(0.5)
|
||||||
.mul(2.0);
|
.mul(2.0);
|
||||||
let data = buff::BuffData::new(1.0, Some(Duration::from_secs_f32(duration)), None);
|
let data = buff::BuffData::new(1.0, Some(Secs(duration)), None);
|
||||||
let source = if let Some(uid) = ecs.read_storage::<Uid>().get(defender) {
|
let source = if let Some(uid) = ecs.read_storage::<Uid>().get(defender) {
|
||||||
BuffSource::Character { by: *uid }
|
BuffSource::Character { by: *uid }
|
||||||
} else {
|
} else {
|
||||||
BuffSource::World
|
BuffSource::World
|
||||||
};
|
};
|
||||||
|
let time = ecs.read_resource::<Time>();
|
||||||
|
let stats = ecs.read_storage::<comp::Stats>();
|
||||||
let buff = buff::Buff::new(
|
let buff = buff::Buff::new(
|
||||||
BuffKind::Parried,
|
BuffKind::Parried,
|
||||||
data,
|
data,
|
||||||
vec![buff::BuffCategory::Physical],
|
vec![buff::BuffCategory::Physical],
|
||||||
source,
|
source,
|
||||||
|
*time,
|
||||||
|
stats.get(attacker),
|
||||||
);
|
);
|
||||||
server_eventbus.emit_now(ServerEvent::Buff {
|
server_eventbus.emit_now(ServerEvent::Buff {
|
||||||
entity: attacker,
|
entity: attacker,
|
||||||
|
@ -23,7 +23,7 @@ use common::{
|
|||||||
effect::Effect,
|
effect::Effect,
|
||||||
link::{Link, LinkHandle},
|
link::{Link, LinkHandle},
|
||||||
mounting::Mounting,
|
mounting::Mounting,
|
||||||
resources::{Time, TimeOfDay},
|
resources::{Secs, Time, TimeOfDay},
|
||||||
slowjob::SlowJobPool,
|
slowjob::SlowJobPool,
|
||||||
uid::{Uid, UidAllocator},
|
uid::{Uid, UidAllocator},
|
||||||
LoadoutBuilder, ViewDistances,
|
LoadoutBuilder, ViewDistances,
|
||||||
@ -38,7 +38,7 @@ use specs::{
|
|||||||
saveload::MarkerAllocator, Builder, Entity as EcsEntity, EntityBuilder as EcsEntityBuilder,
|
saveload::MarkerAllocator, Builder, Entity as EcsEntity, EntityBuilder as EcsEntityBuilder,
|
||||||
Join, WorldExt,
|
Join, WorldExt,
|
||||||
};
|
};
|
||||||
use std::time::{Duration, Instant};
|
use std::time::Instant;
|
||||||
use tracing::{trace, warn};
|
use tracing::{trace, warn};
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
@ -222,16 +222,23 @@ impl StateExt for State {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
Effect::Buff(buff) => {
|
Effect::Buff(buff) => {
|
||||||
|
let time = self.ecs().read_resource::<Time>();
|
||||||
|
let stats = self.ecs().read_storage::<comp::Stats>();
|
||||||
self.ecs()
|
self.ecs()
|
||||||
.write_storage::<comp::Buffs>()
|
.write_storage::<comp::Buffs>()
|
||||||
.get_mut(entity)
|
.get_mut(entity)
|
||||||
.map(|mut buffs| {
|
.map(|mut buffs| {
|
||||||
buffs.insert(comp::Buff::new(
|
buffs.insert(
|
||||||
|
comp::Buff::new(
|
||||||
buff.kind,
|
buff.kind,
|
||||||
buff.data,
|
buff.data,
|
||||||
buff.cat_ids,
|
buff.cat_ids,
|
||||||
comp::BuffSource::Item,
|
comp::BuffSource::Item,
|
||||||
))
|
*time,
|
||||||
|
stats.get(entity),
|
||||||
|
),
|
||||||
|
*time,
|
||||||
|
)
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -425,19 +432,21 @@ impl StateExt for State {
|
|||||||
aura::{Aura, AuraKind, AuraTarget, Auras},
|
aura::{Aura, AuraKind, AuraTarget, Auras},
|
||||||
buff::{BuffCategory, BuffData, BuffKind, BuffSource},
|
buff::{BuffCategory, BuffData, BuffKind, BuffSource},
|
||||||
};
|
};
|
||||||
|
let time = self.get_time();
|
||||||
self.ecs_mut()
|
self.ecs_mut()
|
||||||
.create_entity_synced()
|
.create_entity_synced()
|
||||||
.with(pos)
|
.with(pos)
|
||||||
.with(Auras::new(vec![Aura::new(
|
.with(Auras::new(vec![Aura::new(
|
||||||
AuraKind::Buff {
|
AuraKind::Buff {
|
||||||
kind: BuffKind::Invulnerability,
|
kind: BuffKind::Invulnerability,
|
||||||
data: BuffData::new(1.0, Some(Duration::from_secs(1)), None),
|
data: BuffData::new(1.0, Some(Secs(1.0)), None),
|
||||||
category: BuffCategory::Natural,
|
category: BuffCategory::Natural,
|
||||||
source: BuffSource::World,
|
source: BuffSource::World,
|
||||||
},
|
},
|
||||||
range.unwrap_or(100.0),
|
range.unwrap_or(100.0),
|
||||||
None,
|
None,
|
||||||
AuraTarget::All,
|
AuraTarget::All,
|
||||||
|
Time(time),
|
||||||
)]))
|
)]))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ use common::{
|
|||||||
event::EventBus,
|
event::EventBus,
|
||||||
outcome::Outcome,
|
outcome::Outcome,
|
||||||
region::{Event as RegionEvent, RegionMap},
|
region::{Event as RegionEvent, RegionMap},
|
||||||
resources::{PlayerPhysicsSettings, TimeOfDay},
|
resources::{PlayerPhysicsSettings, Time, TimeOfDay},
|
||||||
terrain::TerrainChunkSize,
|
terrain::TerrainChunkSize,
|
||||||
uid::Uid,
|
uid::Uid,
|
||||||
vol::RectVolSize,
|
vol::RectVolSize,
|
||||||
@ -33,6 +33,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
Read<'a, PlayerPhysicsSettings>,
|
Read<'a, PlayerPhysicsSettings>,
|
||||||
TrackedStorages<'a>,
|
TrackedStorages<'a>,
|
||||||
ReadExpect<'a, TimeOfDay>,
|
ReadExpect<'a, TimeOfDay>,
|
||||||
|
ReadExpect<'a, Time>,
|
||||||
ReadExpect<'a, Calendar>,
|
ReadExpect<'a, Calendar>,
|
||||||
ReadExpect<'a, RegionMap>,
|
ReadExpect<'a, RegionMap>,
|
||||||
ReadExpect<'a, UpdateTrackers>,
|
ReadExpect<'a, UpdateTrackers>,
|
||||||
@ -64,6 +65,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
player_physics_settings,
|
player_physics_settings,
|
||||||
tracked_storages,
|
tracked_storages,
|
||||||
time_of_day,
|
time_of_day,
|
||||||
|
time,
|
||||||
calendar,
|
calendar,
|
||||||
region_map,
|
region_map,
|
||||||
trackers,
|
trackers,
|
||||||
@ -416,7 +418,11 @@ impl<'a> System<'a> for Sys {
|
|||||||
let mut tod_lazymsg = None;
|
let mut tod_lazymsg = None;
|
||||||
for client in (&clients).join() {
|
for client in (&clients).join() {
|
||||||
let msg = tod_lazymsg.unwrap_or_else(|| {
|
let msg = tod_lazymsg.unwrap_or_else(|| {
|
||||||
client.prepare(ServerGeneral::TimeOfDay(*time_of_day, (*calendar).clone()))
|
client.prepare(ServerGeneral::TimeOfDay(
|
||||||
|
*time_of_day,
|
||||||
|
(*calendar).clone(),
|
||||||
|
*time,
|
||||||
|
))
|
||||||
});
|
});
|
||||||
// We don't care much about stream errors here since they could just represent
|
// We don't care much about stream errors here since they could just represent
|
||||||
// network disconnection, which is handled elsewhere.
|
// network disconnection, which is handled elsewhere.
|
||||||
|
@ -13,6 +13,7 @@ use common::{
|
|||||||
cmd::ServerChatCommand,
|
cmd::ServerChatCommand,
|
||||||
comp,
|
comp,
|
||||||
comp::{inventory::item::armor::Friction, Poise, PoiseState},
|
comp::{inventory::item::armor::Friction, Poise, PoiseState},
|
||||||
|
resources::Time,
|
||||||
};
|
};
|
||||||
use core::mem;
|
use core::mem;
|
||||||
use egui::{
|
use egui::{
|
||||||
@ -535,6 +536,7 @@ fn selected_entity_window(
|
|||||||
.join()
|
.join()
|
||||||
.filter(|(e, _, _, _, _, _, _, _, _, _, _, _, _, _, (_, _, _))| e.id() == entity_id)
|
.filter(|(e, _, _, _, _, _, _, _, _, _, _, _, _, _, (_, _, _))| e.id() == entity_id)
|
||||||
{
|
{
|
||||||
|
let time = ecs.read_resource::<Time>();
|
||||||
if let Some(pos) = pos {
|
if let Some(pos) = pos {
|
||||||
if let Some(shape_id) = selected_entity_info.debug_shape_id {
|
if let Some(shape_id) = selected_entity_info.debug_shape_id {
|
||||||
egui_actions.actions.push(EguiAction::DebugShape(
|
egui_actions.actions.push(EguiAction::DebugShape(
|
||||||
@ -645,8 +647,8 @@ fn selected_entity_window(
|
|||||||
buffs.buffs.iter().for_each(|(_, v)| {
|
buffs.buffs.iter().for_each(|(_, v)| {
|
||||||
ui.label(format!("{:?}", v.kind));
|
ui.label(format!("{:?}", v.kind));
|
||||||
ui.label(
|
ui.label(
|
||||||
v.time.map_or("-".to_string(), |time| {
|
v.end_time.map_or("-".to_string(), |end| {
|
||||||
format!("{:?}", time)
|
format!("{:?}", end.0 - time.0)
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
ui.label(format!("{:?}", v.source));
|
ui.label(format!("{:?}", v.source));
|
||||||
@ -672,7 +674,7 @@ fn selected_entity_window(
|
|||||||
Buff { kind, .. } => format!("Buff - {:?}", kind)
|
Buff { kind, .. } => format!("Buff - {:?}", kind)
|
||||||
});
|
});
|
||||||
ui.label(format!("{:1}", v.radius));
|
ui.label(format!("{:1}", v.radius));
|
||||||
ui.label(v.duration.map_or("-".to_owned(), |x| format!("{:1}s", x.as_secs())));
|
ui.label(v.end_time.map_or("-".to_owned(), |x| format!("{:1}s", x.0 - time.0)));
|
||||||
ui.label(format!("{:?}", v.target));
|
ui.label(format!("{:?}", v.target));
|
||||||
ui.end_row();
|
ui.end_row();
|
||||||
});
|
});
|
||||||
|
@ -9,7 +9,10 @@ use crate::{
|
|||||||
};
|
};
|
||||||
use i18n::Localization;
|
use i18n::Localization;
|
||||||
|
|
||||||
use common::comp::{BuffKind, Buffs, CharacterState, Energy, Health};
|
use common::{
|
||||||
|
comp::{BuffKind, Buffs, CharacterState, Energy, Health},
|
||||||
|
resources::Time,
|
||||||
|
};
|
||||||
use conrod_core::{
|
use conrod_core::{
|
||||||
color,
|
color,
|
||||||
image::Id,
|
image::Id,
|
||||||
@ -49,6 +52,7 @@ pub struct BuffsBar<'a> {
|
|||||||
global_state: &'a GlobalState,
|
global_state: &'a GlobalState,
|
||||||
health: &'a Health,
|
health: &'a Health,
|
||||||
energy: &'a Energy,
|
energy: &'a Energy,
|
||||||
|
time: &'a Time,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> BuffsBar<'a> {
|
impl<'a> BuffsBar<'a> {
|
||||||
@ -64,6 +68,7 @@ impl<'a> BuffsBar<'a> {
|
|||||||
global_state: &'a GlobalState,
|
global_state: &'a GlobalState,
|
||||||
health: &'a Health,
|
health: &'a Health,
|
||||||
energy: &'a Energy,
|
energy: &'a Energy,
|
||||||
|
time: &'a Time,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
imgs,
|
imgs,
|
||||||
@ -78,6 +83,7 @@ impl<'a> BuffsBar<'a> {
|
|||||||
global_state,
|
global_state,
|
||||||
health,
|
health,
|
||||||
energy,
|
energy,
|
||||||
|
time,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -219,10 +225,9 @@ impl<'a> Widget for BuffsBar<'a> {
|
|||||||
.enumerate()
|
.enumerate()
|
||||||
.for_each(|(i, (((id, timer_id), mult_id), buff))| {
|
.for_each(|(i, (((id, timer_id), mult_id), buff))| {
|
||||||
let max_duration = buff.kind.max_duration();
|
let max_duration = buff.kind.max_duration();
|
||||||
let current_duration = buff.dur;
|
let current_duration = buff.end_time.map(|end| end - self.time.0);
|
||||||
let duration_percentage = current_duration.map_or(1000.0, |cur| {
|
let duration_percentage = current_duration.map_or(1000.0, |cur| {
|
||||||
max_duration
|
max_duration.map_or(1000.0, |max| cur / max.0 * 1000.0)
|
||||||
.map_or(1000.0, |max| cur.as_secs_f32() / max.as_secs_f32() * 1000.0)
|
|
||||||
}) as u32; // Percentage to determine which frame of the timer overlay is displayed
|
}) as u32; // Percentage to determine which frame of the timer overlay is displayed
|
||||||
let buff_img = buff.kind.image(self.imgs);
|
let buff_img = buff.kind.image(self.imgs);
|
||||||
let buff_widget = Image::new(buff_img).w_h(40.0, 40.0);
|
let buff_widget = Image::new(buff_img).w_h(40.0, 40.0);
|
||||||
@ -236,13 +241,11 @@ impl<'a> Widget for BuffsBar<'a> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
buff_widget
|
buff_widget
|
||||||
.color(
|
.color(if current_duration.map_or(false, |cur| cur < 10.0) {
|
||||||
if current_duration.map_or(false, |cur| cur.as_secs_f32() < 10.0) {
|
|
||||||
Some(pulsating_col)
|
Some(pulsating_col)
|
||||||
} else {
|
} else {
|
||||||
Some(norm_col)
|
Some(norm_col)
|
||||||
},
|
})
|
||||||
)
|
|
||||||
.set(*id, ui);
|
.set(*id, ui);
|
||||||
if buff.multiplicity() > 1 {
|
if buff.multiplicity() > 1 {
|
||||||
Rectangle::fill_with([0.0, 0.0], MULTIPLICITY_COLOR.plain_contrast())
|
Rectangle::fill_with([0.0, 0.0], MULTIPLICITY_COLOR.plain_contrast())
|
||||||
@ -260,7 +263,7 @@ impl<'a> Widget for BuffsBar<'a> {
|
|||||||
}
|
}
|
||||||
// Create Buff tooltip
|
// Create Buff tooltip
|
||||||
let (title, desc_txt) = buff.kind.title_description(localized_strings);
|
let (title, desc_txt) = buff.kind.title_description(localized_strings);
|
||||||
let remaining_time = buff.get_buff_time();
|
let remaining_time = buff.get_buff_time(*self.time);
|
||||||
let click_to_remove =
|
let click_to_remove =
|
||||||
format!("<{}>", &localized_strings.get_msg("buff-remove"));
|
format!("<{}>", &localized_strings.get_msg("buff-remove"));
|
||||||
let desc = format!("{}\n\n{}\n\n{}", desc_txt, remaining_time, click_to_remove);
|
let desc = format!("{}\n\n{}\n\n{}", desc_txt, remaining_time, click_to_remove);
|
||||||
@ -304,10 +307,9 @@ impl<'a> Widget for BuffsBar<'a> {
|
|||||||
.enumerate()
|
.enumerate()
|
||||||
.for_each(|(i, (((id, timer_id), mult_id), debuff))| {
|
.for_each(|(i, (((id, timer_id), mult_id), debuff))| {
|
||||||
let max_duration = debuff.kind.max_duration();
|
let max_duration = debuff.kind.max_duration();
|
||||||
let current_duration = debuff.dur;
|
let current_duration = debuff.end_time.map(|end| end - self.time.0);
|
||||||
let duration_percentage = current_duration.map_or(1000.0, |cur| {
|
let duration_percentage = current_duration.map_or(1000.0, |cur| {
|
||||||
max_duration
|
max_duration.map_or(1000.0, |max| cur / max.0 * 1000.0)
|
||||||
.map_or(1000.0, |max| cur.as_secs_f32() / max.as_secs_f32() * 1000.0)
|
|
||||||
}) as u32; // Percentage to determine which frame of the timer overlay is displayed
|
}) as u32; // Percentage to determine which frame of the timer overlay is displayed
|
||||||
let debuff_img = debuff.kind.image(self.imgs);
|
let debuff_img = debuff.kind.image(self.imgs);
|
||||||
let debuff_widget = Image::new(debuff_img).w_h(40.0, 40.0);
|
let debuff_widget = Image::new(debuff_img).w_h(40.0, 40.0);
|
||||||
@ -321,13 +323,11 @@ impl<'a> Widget for BuffsBar<'a> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
debuff_widget
|
debuff_widget
|
||||||
.color(
|
.color(if current_duration.map_or(false, |cur| cur < 10.0) {
|
||||||
if current_duration.map_or(false, |cur| cur.as_secs_f32() < 10.0) {
|
|
||||||
Some(pulsating_col)
|
Some(pulsating_col)
|
||||||
} else {
|
} else {
|
||||||
Some(norm_col)
|
Some(norm_col)
|
||||||
},
|
})
|
||||||
)
|
|
||||||
.set(*id, ui);
|
.set(*id, ui);
|
||||||
if debuff.multiplicity() > 1 {
|
if debuff.multiplicity() > 1 {
|
||||||
Rectangle::fill_with([0.0, 0.0], MULTIPLICITY_COLOR.plain_contrast())
|
Rectangle::fill_with([0.0, 0.0], MULTIPLICITY_COLOR.plain_contrast())
|
||||||
@ -345,7 +345,7 @@ impl<'a> Widget for BuffsBar<'a> {
|
|||||||
}
|
}
|
||||||
// Create Debuff tooltip
|
// Create Debuff tooltip
|
||||||
let (title, desc_txt) = debuff.kind.title_description(localized_strings);
|
let (title, desc_txt) = debuff.kind.title_description(localized_strings);
|
||||||
let remaining_time = debuff.get_buff_time();
|
let remaining_time = debuff.get_buff_time(*self.time);
|
||||||
let desc = format!("{}\n\n{}", desc_txt, remaining_time);
|
let desc = format!("{}\n\n{}", desc_txt, remaining_time);
|
||||||
Image::new(self.get_duration_image(duration_percentage))
|
Image::new(self.get_duration_image(duration_percentage))
|
||||||
.w_h(40.0, 40.0)
|
.w_h(40.0, 40.0)
|
||||||
@ -405,11 +405,10 @@ impl<'a> Widget for BuffsBar<'a> {
|
|||||||
buff_vec.iter().enumerate().for_each(
|
buff_vec.iter().enumerate().for_each(
|
||||||
|(i, ((((id, timer_id), txt_id), mult_id), buff))| {
|
|(i, ((((id, timer_id), txt_id), mult_id), buff))| {
|
||||||
let max_duration = buff.kind.max_duration();
|
let max_duration = buff.kind.max_duration();
|
||||||
let current_duration = buff.dur;
|
let current_duration = buff.end_time.map(|end| end - self.time.0);
|
||||||
// Percentage to determine which frame of the timer overlay is displayed
|
// Percentage to determine which frame of the timer overlay is displayed
|
||||||
let duration_percentage = current_duration.map_or(1000.0, |cur| {
|
let duration_percentage = current_duration.map_or(1000.0, |cur| {
|
||||||
max_duration
|
max_duration.map_or(1000.0, |max| cur / max.0 * 1000.0)
|
||||||
.map_or(1000.0, |max| cur.as_secs_f32() / max.as_secs_f32() * 1000.0)
|
|
||||||
}) as u32;
|
}) as u32;
|
||||||
let buff_img = buff.kind.image(self.imgs);
|
let buff_img = buff.kind.image(self.imgs);
|
||||||
let buff_widget = Image::new(buff_img).w_h(40.0, 40.0);
|
let buff_widget = Image::new(buff_img).w_h(40.0, 40.0);
|
||||||
@ -422,13 +421,11 @@ impl<'a> Widget for BuffsBar<'a> {
|
|||||||
0.0 + x as f64 * (42.0),
|
0.0 + x as f64 * (42.0),
|
||||||
);
|
);
|
||||||
buff_widget
|
buff_widget
|
||||||
.color(
|
.color(if current_duration.map_or(false, |cur| cur < 10.0) {
|
||||||
if current_duration.map_or(false, |cur| cur.as_secs_f32() < 10.0) {
|
|
||||||
Some(pulsating_col)
|
Some(pulsating_col)
|
||||||
} else {
|
} else {
|
||||||
Some(norm_col)
|
Some(norm_col)
|
||||||
},
|
})
|
||||||
)
|
|
||||||
.set(*id, ui);
|
.set(*id, ui);
|
||||||
if buff.multiplicity() > 1 {
|
if buff.multiplicity() > 1 {
|
||||||
Rectangle::fill_with([0.0, 0.0], MULTIPLICITY_COLOR.plain_contrast())
|
Rectangle::fill_with([0.0, 0.0], MULTIPLICITY_COLOR.plain_contrast())
|
||||||
@ -446,7 +443,7 @@ impl<'a> Widget for BuffsBar<'a> {
|
|||||||
}
|
}
|
||||||
// Create Buff tooltip
|
// Create Buff tooltip
|
||||||
let (title, desc_txt) = buff.kind.title_description(localized_strings);
|
let (title, desc_txt) = buff.kind.title_description(localized_strings);
|
||||||
let remaining_time = buff.get_buff_time();
|
let remaining_time = buff.get_buff_time(*self.time);
|
||||||
let click_to_remove =
|
let click_to_remove =
|
||||||
format!("<{}>", &localized_strings.get_msg("buff-remove"));
|
format!("<{}>", &localized_strings.get_msg("buff-remove"));
|
||||||
let desc = if buff.is_buff {
|
let desc = if buff.is_buff {
|
||||||
|
@ -17,6 +17,7 @@ use client::{self, Client};
|
|||||||
use common::{
|
use common::{
|
||||||
combat,
|
combat,
|
||||||
comp::{group::Role, inventory::item::MaterialStatManifest, invite::InviteKind, Stats},
|
comp::{group::Role, inventory::item::MaterialStatManifest, invite::InviteKind, Stats},
|
||||||
|
resources::Time,
|
||||||
uid::{Uid, UidAllocator},
|
uid::{Uid, UidAllocator},
|
||||||
};
|
};
|
||||||
use common_net::sync::WorldSyncExt;
|
use common_net::sync::WorldSyncExt;
|
||||||
@ -83,6 +84,7 @@ pub struct Group<'a> {
|
|||||||
global_state: &'a GlobalState,
|
global_state: &'a GlobalState,
|
||||||
tooltip_manager: &'a mut TooltipManager,
|
tooltip_manager: &'a mut TooltipManager,
|
||||||
msm: &'a MaterialStatManifest,
|
msm: &'a MaterialStatManifest,
|
||||||
|
time: &'a Time,
|
||||||
|
|
||||||
#[conrod(common_builder)]
|
#[conrod(common_builder)]
|
||||||
common: widget::CommonBuilder,
|
common: widget::CommonBuilder,
|
||||||
@ -101,6 +103,7 @@ impl<'a> Group<'a> {
|
|||||||
global_state: &'a GlobalState,
|
global_state: &'a GlobalState,
|
||||||
tooltip_manager: &'a mut TooltipManager,
|
tooltip_manager: &'a mut TooltipManager,
|
||||||
msm: &'a MaterialStatManifest,
|
msm: &'a MaterialStatManifest,
|
||||||
|
time: &'a Time,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
show,
|
show,
|
||||||
@ -114,6 +117,7 @@ impl<'a> Group<'a> {
|
|||||||
global_state,
|
global_state,
|
||||||
tooltip_manager,
|
tooltip_manager,
|
||||||
msm,
|
msm,
|
||||||
|
time,
|
||||||
common: widget::CommonBuilder::default(),
|
common: widget::CommonBuilder::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -532,11 +536,9 @@ impl<'a> Widget for Group<'a> {
|
|||||||
let max_duration = buff.kind.max_duration();
|
let max_duration = buff.kind.max_duration();
|
||||||
let pulsating_col = Color::Rgba(1.0, 1.0, 1.0, buff_ani);
|
let pulsating_col = Color::Rgba(1.0, 1.0, 1.0, buff_ani);
|
||||||
let norm_col = Color::Rgba(1.0, 1.0, 1.0, 1.0);
|
let norm_col = Color::Rgba(1.0, 1.0, 1.0, 1.0);
|
||||||
let current_duration = buff.dur;
|
let current_duration = buff.end_time.map(|end| end - self.time.0);
|
||||||
let duration_percentage = current_duration.map_or(1000.0, |cur| {
|
let duration_percentage = current_duration.map_or(1000.0, |cur| {
|
||||||
max_duration.map_or(1000.0, |max| {
|
max_duration.map_or(1000.0, |max| cur / max.0 * 1000.0)
|
||||||
cur.as_secs_f32() / max.as_secs_f32() * 1000.0
|
|
||||||
})
|
|
||||||
}) as u32; // Percentage to determine which frame of the timer overlay is displayed
|
}) as u32; // Percentage to determine which frame of the timer overlay is displayed
|
||||||
let buff_img = buff.kind.image(self.imgs);
|
let buff_img = buff.kind.image(self.imgs);
|
||||||
let buff_widget = Image::new(buff_img).w_h(15.0, 15.0);
|
let buff_widget = Image::new(buff_img).w_h(15.0, 15.0);
|
||||||
@ -551,20 +553,16 @@ impl<'a> Widget for Group<'a> {
|
|||||||
};
|
};
|
||||||
prev_id = Some(id);
|
prev_id = Some(id);
|
||||||
buff_widget
|
buff_widget
|
||||||
.color(
|
.color(if current_duration.map_or(false, |cur| cur < 10.0) {
|
||||||
if current_duration
|
|
||||||
.map_or(false, |cur| cur.as_secs_f32() < 10.0)
|
|
||||||
{
|
|
||||||
Some(pulsating_col)
|
Some(pulsating_col)
|
||||||
} else {
|
} else {
|
||||||
Some(norm_col)
|
Some(norm_col)
|
||||||
},
|
})
|
||||||
)
|
|
||||||
.set(id, ui);
|
.set(id, ui);
|
||||||
// Create Buff tooltip
|
// Create Buff tooltip
|
||||||
let (title, desc_txt) =
|
let (title, desc_txt) =
|
||||||
buff.kind.title_description(localized_strings);
|
buff.kind.title_description(localized_strings);
|
||||||
let remaining_time = buff.get_buff_time();
|
let remaining_time = buff.get_buff_time(*self.time);
|
||||||
let desc = format!("{}\n\n{}", desc_txt, remaining_time);
|
let desc = format!("{}\n\n{}", desc_txt, remaining_time);
|
||||||
Image::new(match duration_percentage as u64 {
|
Image::new(match duration_percentage as u64 {
|
||||||
875..=1000 => self.imgs.nothing, // 8/8
|
875..=1000 => self.imgs.nothing, // 8/8
|
||||||
|
@ -104,6 +104,7 @@ use common::{
|
|||||||
link::Is,
|
link::Is,
|
||||||
mounting::Mount,
|
mounting::Mount,
|
||||||
outcome::Outcome,
|
outcome::Outcome,
|
||||||
|
resources::{Secs, Time},
|
||||||
slowjob::SlowJobPool,
|
slowjob::SlowJobPool,
|
||||||
terrain::{SpriteKind, TerrainChunk, UnlockKind},
|
terrain::{SpriteKind, TerrainChunk, UnlockKind},
|
||||||
trade::{ReducedInventory, TradeAction},
|
trade::{ReducedInventory, TradeAction},
|
||||||
@ -481,7 +482,7 @@ impl<'a> BuffIconKind<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn max_duration(&self) -> Option<Duration> {
|
pub fn max_duration(&self) -> Option<Secs> {
|
||||||
match self {
|
match self {
|
||||||
Self::Buff { data, .. } => data.duration,
|
Self::Buff { data, .. } => data.duration,
|
||||||
Self::Ability { .. } => None,
|
Self::Ability { .. } => None,
|
||||||
@ -563,7 +564,7 @@ impl<'a> Eq for BuffIconKind<'a> {}
|
|||||||
pub struct BuffIcon<'a> {
|
pub struct BuffIcon<'a> {
|
||||||
kind: BuffIconKind<'a>,
|
kind: BuffIconKind<'a>,
|
||||||
is_buff: bool,
|
is_buff: bool,
|
||||||
dur: Option<Duration>,
|
end_time: Option<f64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> BuffIcon<'a> {
|
impl<'a> BuffIcon<'a> {
|
||||||
@ -574,9 +575,9 @@ impl<'a> BuffIcon<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_buff_time(&self) -> String {
|
pub fn get_buff_time(&self, time: Time) -> String {
|
||||||
if let Some(dur) = self.dur {
|
if let Some(end) = self.end_time {
|
||||||
format!("{:.0}s", dur.as_secs_f32())
|
format!("{:.0}s", end - time.0)
|
||||||
} else {
|
} else {
|
||||||
"".to_string()
|
"".to_string()
|
||||||
}
|
}
|
||||||
@ -600,7 +601,7 @@ impl<'a> BuffIcon<'a> {
|
|||||||
Some(BuffIcon {
|
Some(BuffIcon {
|
||||||
kind: BuffIconKind::Ability { ability_id: id },
|
kind: BuffIconKind::Ability { ability_id: id },
|
||||||
is_buff: true,
|
is_buff: true,
|
||||||
dur: None,
|
end_time: None,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
@ -619,7 +620,7 @@ impl<'a> BuffIcon<'a> {
|
|||||||
multiplicity: count,
|
multiplicity: count,
|
||||||
},
|
},
|
||||||
is_buff: buff.kind.is_buff(),
|
is_buff: buff.kind.is_buff(),
|
||||||
dur: buff.time,
|
end_time: buff.end_time.map(|end| end.0),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1491,6 +1492,7 @@ impl Hud {
|
|||||||
let alignments = ecs.read_storage::<comp::Alignment>();
|
let alignments = ecs.read_storage::<comp::Alignment>();
|
||||||
let is_mount = ecs.read_storage::<Is<Mount>>();
|
let is_mount = ecs.read_storage::<Is<Mount>>();
|
||||||
let char_states = ecs.read_storage::<comp::CharacterState>();
|
let char_states = ecs.read_storage::<comp::CharacterState>();
|
||||||
|
let time = ecs.read_resource::<Time>();
|
||||||
|
|
||||||
// Check if there was a persistence load error of the skillset, and if so
|
// Check if there was a persistence load error of the skillset, and if so
|
||||||
// display a dialog prompt
|
// display a dialog prompt
|
||||||
@ -2335,6 +2337,7 @@ impl Hud {
|
|||||||
},
|
},
|
||||||
_ => Vec::new(),
|
_ => Vec::new(),
|
||||||
},
|
},
|
||||||
|
&time,
|
||||||
)
|
)
|
||||||
.x_y(0.0, 100.0)
|
.x_y(0.0, 100.0)
|
||||||
.position_ingame(ingame_pos)
|
.position_ingame(ingame_pos)
|
||||||
@ -2814,6 +2817,7 @@ impl Hud {
|
|||||||
let buffs = ecs.read_storage::<comp::Buffs>();
|
let buffs = ecs.read_storage::<comp::Buffs>();
|
||||||
let char_states = ecs.read_storage::<comp::CharacterState>();
|
let char_states = ecs.read_storage::<comp::CharacterState>();
|
||||||
let msm = ecs.read_resource::<MaterialStatManifest>();
|
let msm = ecs.read_resource::<MaterialStatManifest>();
|
||||||
|
let time = ecs.read_resource::<Time>();
|
||||||
|
|
||||||
match Buttons::new(
|
match Buttons::new(
|
||||||
&self.imgs,
|
&self.imgs,
|
||||||
@ -2845,6 +2849,7 @@ impl Hud {
|
|||||||
global_state,
|
global_state,
|
||||||
tooltip_manager,
|
tooltip_manager,
|
||||||
&msm,
|
&msm,
|
||||||
|
&time,
|
||||||
)
|
)
|
||||||
.set(self.ids.group_window, ui_widgets)
|
.set(self.ids.group_window, ui_widgets)
|
||||||
{
|
{
|
||||||
@ -2928,6 +2933,7 @@ impl Hud {
|
|||||||
let bodies = ecs.read_storage::<comp::Body>();
|
let bodies = ecs.read_storage::<comp::Body>();
|
||||||
let poises = ecs.read_storage::<comp::Poise>();
|
let poises = ecs.read_storage::<comp::Poise>();
|
||||||
let combos = ecs.read_storage::<comp::Combo>();
|
let combos = ecs.read_storage::<comp::Combo>();
|
||||||
|
let time = ecs.read_resource::<Time>();
|
||||||
// Combo floater stuffs
|
// Combo floater stuffs
|
||||||
self.floaters.combo_floater = self.floaters.combo_floater.map(|mut f| {
|
self.floaters.combo_floater = self.floaters.combo_floater.map(|mut f| {
|
||||||
f.timer -= dt.as_secs_f64();
|
f.timer -= dt.as_secs_f64();
|
||||||
@ -3116,6 +3122,7 @@ impl Hud {
|
|||||||
global_state,
|
global_state,
|
||||||
health,
|
health,
|
||||||
energy,
|
energy,
|
||||||
|
&time,
|
||||||
)
|
)
|
||||||
.set(self.ids.buffs, ui_widgets)
|
.set(self.ids.buffs, ui_widgets)
|
||||||
{
|
{
|
||||||
|
@ -9,7 +9,10 @@ use crate::{
|
|||||||
settings::{ControlSettings, InterfaceSettings},
|
settings::{ControlSettings, InterfaceSettings},
|
||||||
ui::{fonts::Fonts, Ingameable},
|
ui::{fonts::Fonts, Ingameable},
|
||||||
};
|
};
|
||||||
use common::comp::{Buffs, CharacterState, Energy, Health, SpeechBubble, SpeechBubbleType};
|
use common::{
|
||||||
|
comp::{Buffs, CharacterState, Energy, Health, SpeechBubble, SpeechBubbleType},
|
||||||
|
resources::Time,
|
||||||
|
};
|
||||||
use conrod_core::{
|
use conrod_core::{
|
||||||
color,
|
color,
|
||||||
position::Align,
|
position::Align,
|
||||||
@ -96,6 +99,7 @@ pub struct Overhead<'a> {
|
|||||||
fonts: &'a Fonts,
|
fonts: &'a Fonts,
|
||||||
key_layout: &'a Option<KeyLayout>,
|
key_layout: &'a Option<KeyLayout>,
|
||||||
interaction_options: Vec<(GameInput, String)>,
|
interaction_options: Vec<(GameInput, String)>,
|
||||||
|
time: &'a Time,
|
||||||
|
|
||||||
#[conrod(common_builder)]
|
#[conrod(common_builder)]
|
||||||
common: widget::CommonBuilder,
|
common: widget::CommonBuilder,
|
||||||
@ -115,6 +119,7 @@ impl<'a> Overhead<'a> {
|
|||||||
fonts: &'a Fonts,
|
fonts: &'a Fonts,
|
||||||
key_layout: &'a Option<KeyLayout>,
|
key_layout: &'a Option<KeyLayout>,
|
||||||
interaction_options: Vec<(GameInput, String)>,
|
interaction_options: Vec<(GameInput, String)>,
|
||||||
|
time: &'a Time,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
info,
|
info,
|
||||||
@ -128,6 +133,7 @@ impl<'a> Overhead<'a> {
|
|||||||
fonts,
|
fonts,
|
||||||
key_layout,
|
key_layout,
|
||||||
interaction_options,
|
interaction_options,
|
||||||
|
time,
|
||||||
common: widget::CommonBuilder::default(),
|
common: widget::CommonBuilder::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -264,11 +270,9 @@ impl<'a> Widget for Overhead<'a> {
|
|||||||
.for_each(|(i, ((id, timer_id), buff))| {
|
.for_each(|(i, ((id, timer_id), buff))| {
|
||||||
// Limit displayed buffs
|
// Limit displayed buffs
|
||||||
let max_duration = buff.kind.max_duration();
|
let max_duration = buff.kind.max_duration();
|
||||||
let current_duration = buff.dur;
|
let current_duration = buff.end_time.map(|end| end - self.time.0);
|
||||||
let duration_percentage = current_duration.map_or(1000.0, |cur| {
|
let duration_percentage = current_duration.map_or(1000.0, |cur| {
|
||||||
max_duration.map_or(1000.0, |max| {
|
max_duration.map_or(1000.0, |max| cur / max.0 * 1000.0)
|
||||||
cur.as_secs_f32() / max.as_secs_f32() * 1000.0
|
|
||||||
})
|
|
||||||
}) as u32; // Percentage to determine which frame of the timer overlay is displayed
|
}) as u32; // Percentage to determine which frame of the timer overlay is displayed
|
||||||
let buff_img = buff.kind.image(self.imgs);
|
let buff_img = buff.kind.image(self.imgs);
|
||||||
let buff_widget = Image::new(buff_img).w_h(20.0, 20.0);
|
let buff_widget = Image::new(buff_img).w_h(20.0, 20.0);
|
||||||
@ -281,13 +285,11 @@ impl<'a> Widget for Overhead<'a> {
|
|||||||
0.0 + x as f64 * (21.0),
|
0.0 + x as f64 * (21.0),
|
||||||
);
|
);
|
||||||
buff_widget
|
buff_widget
|
||||||
.color(
|
.color(if current_duration.map_or(false, |cur| cur < 10.0) {
|
||||||
if current_duration.map_or(false, |cur| cur.as_secs_f32() < 10.0) {
|
|
||||||
Some(pulsating_col)
|
Some(pulsating_col)
|
||||||
} else {
|
} else {
|
||||||
Some(norm_col)
|
Some(norm_col)
|
||||||
},
|
})
|
||||||
)
|
|
||||||
.set(id, ui);
|
.set(id, ui);
|
||||||
|
|
||||||
Image::new(match duration_percentage as u64 {
|
Image::new(match duration_percentage as u64 {
|
||||||
|
@ -146,7 +146,7 @@ pub fn consumable_desc(effects: &[Effect], i18n: &Localization) -> Vec<String> {
|
|||||||
let mut description = String::new();
|
let mut description = String::new();
|
||||||
if let Effect::Buff(buff) = effect {
|
if let Effect::Buff(buff) = effect {
|
||||||
let strength = buff.data.strength;
|
let strength = buff.data.strength;
|
||||||
let dur_secs = buff.data.duration.map(|d| d.as_secs_f32());
|
let dur_secs = buff.data.duration.map(|d| d.0 as f32);
|
||||||
let str_total = dur_secs.map_or(strength, |secs| strength * secs);
|
let str_total = dur_secs.map_or(strength, |secs| strength * secs);
|
||||||
|
|
||||||
let format_float =
|
let format_float =
|
||||||
|
@ -16,7 +16,7 @@ use common::{
|
|||||||
},
|
},
|
||||||
figure::Segment,
|
figure::Segment,
|
||||||
outcome::Outcome,
|
outcome::Outcome,
|
||||||
resources::DeltaTime,
|
resources::{DeltaTime, Time},
|
||||||
spiral::Spiral2d,
|
spiral::Spiral2d,
|
||||||
states::{self, utils::StageSection},
|
states::{self, utils::StageSection},
|
||||||
terrain::{Block, SpriteKind, TerrainChunk, TerrainGrid},
|
terrain::{Block, SpriteKind, TerrainChunk, TerrainGrid},
|
||||||
@ -1113,9 +1113,13 @@ impl ParticleMgr {
|
|||||||
|| {
|
|| {
|
||||||
let rand_dist = aura.radius * (1.0 - rng.gen::<f32>().powi(100));
|
let rand_dist = aura.radius * (1.0 - rng.gen::<f32>().powi(100));
|
||||||
let init_pos = Vec3::new(rand_dist, 0_f32, 0_f32);
|
let init_pos = Vec3::new(rand_dist, 0_f32, 0_f32);
|
||||||
let max_dur = Duration::from_secs(1);
|
let duration = Duration::from_secs_f64(
|
||||||
|
aura.end_time
|
||||||
|
.map_or(1.0, |end| end.0 - time)
|
||||||
|
.clamp(0.0, 1.0),
|
||||||
|
);
|
||||||
Particle::new_directed(
|
Particle::new_directed(
|
||||||
aura.duration.map_or(max_dur, |dur| dur.min(max_dur)),
|
duration,
|
||||||
time,
|
time,
|
||||||
ParticleMode::EnergyNature,
|
ParticleMode::EnergyNature,
|
||||||
pos,
|
pos,
|
||||||
@ -1145,9 +1149,13 @@ impl ParticleMgr {
|
|||||||
|| {
|
|| {
|
||||||
let rand_dist = aura.radius * (1.0 - rng.gen::<f32>().powi(100));
|
let rand_dist = aura.radius * (1.0 - rng.gen::<f32>().powi(100));
|
||||||
let init_pos = Vec3::new(rand_dist, 0_f32, 0_f32);
|
let init_pos = Vec3::new(rand_dist, 0_f32, 0_f32);
|
||||||
let max_dur = Duration::from_secs(1);
|
let duration = Duration::from_secs_f64(
|
||||||
|
aura.end_time
|
||||||
|
.map_or(1.0, |end| end.0 - time)
|
||||||
|
.clamp(0.0, 1.0),
|
||||||
|
);
|
||||||
Particle::new_directed(
|
Particle::new_directed(
|
||||||
aura.duration.map_or(max_dur, |dur| dur.min(max_dur)),
|
duration,
|
||||||
time,
|
time,
|
||||||
ParticleMode::EnergyHealing,
|
ParticleMode::EnergyHealing,
|
||||||
pos,
|
pos,
|
||||||
@ -1172,9 +1180,13 @@ impl ParticleMgr {
|
|||||||
let y = radius * theta.cos();
|
let y = radius * theta.cos();
|
||||||
Vec2::new(x, y) + pos.xy()
|
Vec2::new(x, y) + pos.xy()
|
||||||
};
|
};
|
||||||
let max_dur = Duration::from_secs(1);
|
let duration = Duration::from_secs_f64(
|
||||||
|
aura.end_time
|
||||||
|
.map_or(1.0, |end| end.0 - time)
|
||||||
|
.clamp(0.0, 1.0),
|
||||||
|
);
|
||||||
Particle::new_directed(
|
Particle::new_directed(
|
||||||
aura.duration.map_or(max_dur, |dur| dur.min(max_dur)),
|
duration,
|
||||||
time,
|
time,
|
||||||
ParticleMode::FlameThrower,
|
ParticleMode::FlameThrower,
|
||||||
rand_pos.with_z(pos.z),
|
rand_pos.with_z(pos.z),
|
||||||
@ -1193,9 +1205,13 @@ impl ParticleMgr {
|
|||||||
|| {
|
|| {
|
||||||
let rand_dist = aura.radius * (1.0 - rng.gen::<f32>().powi(100));
|
let rand_dist = aura.radius * (1.0 - rng.gen::<f32>().powi(100));
|
||||||
let init_pos = Vec3::new(rand_dist, 0_f32, 0_f32);
|
let init_pos = Vec3::new(rand_dist, 0_f32, 0_f32);
|
||||||
let max_dur = Duration::from_secs(1);
|
let duration = Duration::from_secs_f64(
|
||||||
|
aura.end_time
|
||||||
|
.map_or(1.0, |end| end.0 - time)
|
||||||
|
.clamp(0.0, 1.0),
|
||||||
|
);
|
||||||
Particle::new_directed(
|
Particle::new_directed(
|
||||||
aura.duration.map_or(max_dur, |dur| dur.min(max_dur)),
|
duration,
|
||||||
time,
|
time,
|
||||||
ParticleMode::EnergyBuffing,
|
ParticleMode::EnergyBuffing,
|
||||||
pos,
|
pos,
|
||||||
@ -1209,9 +1225,8 @@ impl ParticleMgr {
|
|||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
let is_new_aura = aura.data.duration.map_or(true, |max_dur| {
|
let is_new_aura = aura.data.duration.map_or(true, |max_dur| {
|
||||||
aura.duration.map_or(true, |rem_dur| {
|
let rem_dur = aura.end_time.map_or(time, |e| e.0) - time;
|
||||||
rem_dur.as_secs_f32() > max_dur.as_secs_f32() * 0.9
|
rem_dur > max_dur.0 * 0.9
|
||||||
})
|
|
||||||
});
|
});
|
||||||
if is_new_aura {
|
if is_new_aura {
|
||||||
let heartbeats = self.scheduler.heartbeats(Duration::from_millis(5));
|
let heartbeats = self.scheduler.heartbeats(Duration::from_millis(5));
|
||||||
@ -1297,11 +1312,11 @@ impl ParticleMgr {
|
|||||||
let mut multiplicity = 0;
|
let mut multiplicity = 0;
|
||||||
// Only show particles for potion sickness at the beginning, after the
|
// Only show particles for potion sickness at the beginning, after the
|
||||||
// drinking animation finishes
|
// drinking animation finishes
|
||||||
if buff_ids
|
if buff_ids.0
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|id| buffs.buffs.get(id))
|
.filter_map(|id| buffs.buffs.get(id))
|
||||||
.any(|buff| {
|
.any(|buff| {
|
||||||
matches!(buff.elapsed(), Some(dur) if Duration::from_secs(1) <= dur && dur <= Duration::from_secs_f32(1.5))
|
matches!(buff.elapsed(Time(time)), dur if (1.0..=1.5).contains(&dur.0))
|
||||||
})
|
})
|
||||||
{
|
{
|
||||||
multiplicity = 1;
|
multiplicity = 1;
|
||||||
|
Reference in New Issue
Block a user