diff --git a/build.sbt b/build.sbt index e26b3a0c5..1c374839e 100644 --- a/build.sbt +++ b/build.sbt @@ -66,7 +66,7 @@ lazy val psforeverSettings = Seq( "io.kamon" %% "kamon-apm-reporter" % "2.1.20", "org.json4s" %% "json4s-native" % "4.0.1", "io.getquill" %% "quill-jasync-postgres" % "3.7.2", - "org.flywaydb" % "flyway-core" % "7.10.0", + "org.flywaydb" % "flyway-core" % "7.11.0", "org.postgresql" % "postgresql" % "42.2.22", "com.typesafe" % "config" % "1.4.1", "com.github.pureconfig" %% "pureconfig" % "0.16.0", diff --git a/src/main/scala/net/psforever/actors/session/AvatarActor.scala b/src/main/scala/net/psforever/actors/session/AvatarActor.scala index b06078a6a..f49912835 100644 --- a/src/main/scala/net/psforever/actors/session/AvatarActor.scala +++ b/src/main/scala/net/psforever/actors/session/AvatarActor.scala @@ -142,6 +142,9 @@ object AvatarActor { /** Deinitialize implants (before zoning or respawning) */ final case class DeinitializeImplants() extends Command + /** Deinitialize a certain implant, then initialize it again */ + final case class ResetImplant(implant: ImplantType) extends Command + /** Shorthand for DeinitializeImplants and InitializeImplants */ final case class ResetImplants() extends Command @@ -174,7 +177,7 @@ object AvatarActor { private case class ServiceManagerLookupResult(result: ServiceManager.LookupResult) extends Command - private case class SetStamina(stamina: Int) extends Command + final case class SetStamina(stamina: Int) extends Command private case class SetImplantInitialized(implantType: ImplantType) extends Command @@ -869,8 +872,7 @@ class AvatarActor( Behaviors.same case RestoreStamina(stamina) => - assert(stamina > 0) - if (session.get.player.HasGUID) { + if (stamina > 0 && session.get.player.HasGUID) { val totalStamina = math.min(avatar.maxStamina, avatar.stamina + stamina) val fatigued = if (avatar.fatigued && totalStamina >= 20) { avatar.implants.zipWithIndex.foreach { @@ -892,8 +894,11 @@ class AvatarActor( Behaviors.same case ConsumeStamina(stamina) => - assert(stamina > 0, s"consumed stamina must be larger than 0, but is: $stamina") - consumeThisMuchStamina(stamina) + if (stamina > 0) { + consumeThisMuchStamina(stamina) + } else { + log.warn(s"consumed stamina must be larger than 0, but is: $stamina") + } Behaviors.same case SuspendStaminaRegeneration(duration) => @@ -915,6 +920,10 @@ class AvatarActor( deinitializeImplants() Behaviors.same + case ResetImplant(implantType) => + resetAnImplant(implantType) + Behaviors.same + case ResetImplants() => deinitializeImplants() initializeImplants() @@ -1138,6 +1147,42 @@ class AvatarActor( }) } + def resetAnImplant(implantType: ImplantType): Unit = { + avatar.implants.zipWithIndex.find { + case (Some(imp), _) => imp.definition.implantType == implantType + case (None, _) => false + } match { + case Some((Some(imp), index)) => + //deactivate + if (imp.active) { + deactivateImplant(implantType) + } + //deinitialize + session.get.zone.AvatarEvents ! AvatarServiceMessage( + session.get.zone.id, + AvatarAction.SendResponse( + Service.defaultPlayerGUID, + AvatarImplantMessage(session.get.player.GUID, ImplantAction.Initialization, index, 0) + ) + ) + avatar = avatar.copy( + implants = avatar.implants.updated(index, Some(imp.copy(initialized = false, active = false))) + ) + //restart initialization process + implantTimers(index).cancel() + implantTimers(index) = context.scheduleOnce( + imp.definition.InitializationDuration.seconds, + context.self, + SetImplantInitialized(implantType) + ) + session.get.zone.AvatarEvents ! AvatarServiceMessage( + avatar.name, + AvatarAction.SendResponse(Service.defaultPlayerGUID, ActionProgressMessage(index + 6, 0)) + ) + case _ => ; + } + } + def deactivateImplant(implantType: ImplantType): Unit = { avatar.implants.zipWithIndex.collectFirst { case (Some(implant), index) if implant.definition.implantType == implantType => (implant, index) diff --git a/src/main/scala/net/psforever/actors/session/SessionActor.scala b/src/main/scala/net/psforever/actors/session/SessionActor.scala index 31ed92b05..a7a6a7f5e 100644 --- a/src/main/scala/net/psforever/actors/session/SessionActor.scala +++ b/src/main/scala/net/psforever/actors/session/SessionActor.scala @@ -7710,19 +7710,32 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con None } else { projectile.Resolve() - val outProjectile = if (projectile.profile.ProjectileDamageTypes.contains(DamageType.Aggravated)) { - val quality = projectile.profile.Aggravated match { - case Some(aggravation) - if aggravation.targets.exists(validation => validation.test(target)) && - aggravation.info.exists(_.damage_type == AggravatedDamage.basicDamageType(resolution)) => - ProjectileQuality.AggravatesTarget - case _ => - ProjectileQuality.Normal + val outProjectile = + if (projectile.profile.ProjectileDamageTypes.contains(DamageType.Aggravated)) { + //aggravated + val quality = projectile.profile.Aggravated match { + case Some(aggravation) + if aggravation.targets.exists(validation => validation.test(target)) && + aggravation.info.exists(_.damage_type == AggravatedDamage.basicDamageType(resolution)) => + ProjectileQuality.AggravatesTarget + case _ => + ProjectileQuality.Normal + } + projectile.quality(quality) + } else if (projectile.tool_def.Size == EquipmentSize.Melee) { + //melee + val quality = player.avatar.implants.flatten.find { entry => entry.definition == GlobalDefinitions.melee_booster } match { + case Some(booster) if booster.active && player.avatar.stamina > 9 => + avatarActor ! AvatarActor.ConsumeStamina(10) + ProjectileQuality.Modified(25f) + case _ => + ProjectileQuality.Normal + } + projectile.quality(quality) + } else { + //normal + projectile } - projectile.quality(quality) - } else { - projectile - } Some(DamageInteraction(SourceEntry(target), ProjectileReason(resolution, outProjectile, target.DamageModel), pos)) } } diff --git a/src/main/scala/net/psforever/objects/GlobalDefinitions.scala b/src/main/scala/net/psforever/objects/GlobalDefinitions.scala index 6d6bfa971..0f5f8840e 100644 --- a/src/main/scala/net/psforever/objects/GlobalDefinitions.scala +++ b/src/main/scala/net/psforever/objects/GlobalDefinitions.scala @@ -2415,7 +2415,7 @@ object GlobalDefinitions { chainblade_projectile.InitialVelocity = 100 chainblade_projectile.Lifespan = .02f ProjectileDefinition.CalculateDerivedFields(chainblade_projectile) - chainblade_projectile.Modifiers = MaxDistanceCutoff + chainblade_projectile.Modifiers = List(MeleeBoosted, MaxDistanceCutoff) colossus_100mm_projectile.Name = "colossus_100mm_projectile" colossus_100mm_projectile.Damage0 = 58 @@ -2766,7 +2766,7 @@ object GlobalDefinitions { forceblade_projectile.InitialVelocity = 100 forceblade_projectile.Lifespan = .02f ProjectileDefinition.CalculateDerivedFields(forceblade_projectile) - forceblade_projectile.Modifiers = MaxDistanceCutoff + forceblade_projectile.Modifiers = List(MeleeBoosted, MaxDistanceCutoff) frag_cartridge_projectile.Name = "frag_cartridge_projectile" // TODO for later, maybe : set_resource_parent frag_cartridge_projectile game_objects frag_grenade_projectile @@ -3243,7 +3243,7 @@ object GlobalDefinitions { magcutter_projectile.InitialVelocity = 100 magcutter_projectile.Lifespan = .02f ProjectileDefinition.CalculateDerivedFields(magcutter_projectile) - magcutter_projectile.Modifiers = MaxDistanceCutoff + magcutter_projectile.Modifiers = List(MeleeBoosted, MaxDistanceCutoff) melee_ammo_projectile.Name = "melee_ammo_projectile" melee_ammo_projectile.Damage0 = 25 @@ -3252,7 +3252,7 @@ object GlobalDefinitions { melee_ammo_projectile.InitialVelocity = 100 melee_ammo_projectile.Lifespan = .02f ProjectileDefinition.CalculateDerivedFields(melee_ammo_projectile) - melee_ammo_projectile.Modifiers = MaxDistanceCutoff + melee_ammo_projectile.Modifiers = List(MeleeBoosted, MaxDistanceCutoff) meteor_common.Name = "meteor_common" meteor_common.DamageAtEdge = .1f diff --git a/src/main/scala/net/psforever/objects/avatar/PlayerControl.scala b/src/main/scala/net/psforever/objects/avatar/PlayerControl.scala index 85f4af653..7d75e8281 100644 --- a/src/main/scala/net/psforever/objects/avatar/PlayerControl.scala +++ b/src/main/scala/net/psforever/objects/avatar/PlayerControl.scala @@ -748,8 +748,22 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm } //choose if (target.Health > 0) { + //alive + if (target.Health <= 25 && target.Health + damageToHealth > 25 && + (player.avatar.implants.flatten.find { _.definition.implantType == ImplantType.SecondWind } match { + case Some(wind) => wind.initialized + case _ => false + })) { + //activate second wind + player.Health += 25 + player.History(HealFromImplant(PlayerSource(player), 25, ImplantType.SecondWind)) + avatarActor ! AvatarActor.ResetImplant(ImplantType.SecondWind) + avatarActor ! AvatarActor.RestoreStamina(25) + } + //take damage/update DamageAwareness(target, cause, damageToHealth, damageToArmor, damageToStamina, damageToCapacitor) } else { + //ded DestructionAwareness(target, cause) } } diff --git a/src/main/scala/net/psforever/objects/vital/base/DamageResolution.scala b/src/main/scala/net/psforever/objects/vital/base/DamageResolution.scala index 921607821..b38a92f1c 100644 --- a/src/main/scala/net/psforever/objects/vital/base/DamageResolution.scala +++ b/src/main/scala/net/psforever/objects/vital/base/DamageResolution.scala @@ -28,6 +28,8 @@ object DamageResolution extends Enumeration { AggravatedDirectBurn, //continuous direct hit aggravated damage AggravatedSplash, //splashed aggravated damage AggravatedSplashBurn, //continuous splashed aggravated damage - Explosion //area of effect damage caused by an internal mechanism; unrelated to Splash + Explosion, //area of effect damage caused by an internal mechanism; unrelated to Splash + Environmental, //died to environmental causes + Suicide //i don't want to be the one the battles always choose = Value } diff --git a/src/main/scala/net/psforever/objects/vital/environment/EnvironmentReason.scala b/src/main/scala/net/psforever/objects/vital/environment/EnvironmentReason.scala index 126aae111..3f1f37ed2 100644 --- a/src/main/scala/net/psforever/objects/vital/environment/EnvironmentReason.scala +++ b/src/main/scala/net/psforever/objects/vital/environment/EnvironmentReason.scala @@ -17,7 +17,7 @@ import net.psforever.objects.vital.{NoResistanceSelection, SimpleResolutions, Vi * @param against for the purposes of damage, what kind of target is being acted upon */ final case class EnvironmentReason(body: PieceOfEnvironment, against: DamageCalculations.Selector) extends DamageReason { - def resolution: DamageResolution.Value = DamageResolution.Hit + def resolution: DamageResolution.Value = DamageResolution.Environmental def source: DamageProperties = EnvironmentReason.selectDamage(body) diff --git a/src/main/scala/net/psforever/objects/vital/etc/SuicideReason.scala b/src/main/scala/net/psforever/objects/vital/etc/SuicideReason.scala index 8dff90bf4..1d2f4e8c1 100644 --- a/src/main/scala/net/psforever/objects/vital/etc/SuicideReason.scala +++ b/src/main/scala/net/psforever/objects/vital/etc/SuicideReason.scala @@ -33,7 +33,7 @@ final case class SuicideReason() */ def source: DamageProperties = SuicideReason.damageProperties - def resolution: DamageResolution.Value = DamageResolution.Resolved + def resolution: DamageResolution.Value = DamageResolution.Suicide def same(test: DamageReason): Boolean = { test.source eq source diff --git a/src/main/scala/net/psforever/objects/vital/projectile/ProjectileDamageModifierFunctions.scala b/src/main/scala/net/psforever/objects/vital/projectile/ProjectileDamageModifierFunctions.scala index 4a4b532ec..d8411ea90 100644 --- a/src/main/scala/net/psforever/objects/vital/projectile/ProjectileDamageModifierFunctions.scala +++ b/src/main/scala/net/psforever/objects/vital/projectile/ProjectileDamageModifierFunctions.scala @@ -293,6 +293,19 @@ case object FlakBurst extends ProjectileDamageModifiers.Mod { } } +/** + * If the damage is resolved by way of a melee weapon, + * the damage might be increased if the attack was initiated + * while the attacker was under the effect of an active Melee Boost implant. + * @see `GlobalDefinitions.melee_booster` + * @see `ProjectileQuality` + */ +case object MeleeBoosted extends ProjectileDamageModifiers.Mod { + override def calculate(damage: Int, data: DamageInteraction, cause: ProjectileReason): Int = { + cause.projectile.quality.mod.toInt + damage + } +} + /* Functions */ object ProjectileDamageModifierFunctions { /**