diff --git a/common/src/main/scala/net/psforever/objects/avatar/Aura.scala b/common/src/main/scala/net/psforever/objects/avatar/Aura.scala new file mode 100644 index 00000000..935dce50 --- /dev/null +++ b/common/src/main/scala/net/psforever/objects/avatar/Aura.scala @@ -0,0 +1,10 @@ +// Copyright (c) 2020 PSForever +package net.psforever.objects.avatar + +object Aura extends Enumeration { + final val None = Value(0) + final val Plasma = Value(1) + final val Comet = Value(2) + final val Napalm = Value(4) + final val Fire = Value(8) +} diff --git a/common/src/main/scala/net/psforever/objects/avatar/AuraEffectBehavior.scala b/common/src/main/scala/net/psforever/objects/avatar/AuraEffectBehavior.scala new file mode 100644 index 00000000..3a467a94 --- /dev/null +++ b/common/src/main/scala/net/psforever/objects/avatar/AuraEffectBehavior.scala @@ -0,0 +1,130 @@ +// Copyright (c) 2020 PSForever +package net.psforever.objects.avatar + +import akka.actor.{Actor, Cancellable} +import net.psforever.objects.Player +import net.psforever.objects.ballistics.{AggravatedDamage, AggravatedInfo, ResolvedProjectile} +import net.psforever.objects.vital.DamageType + +import scala.collection.mutable +import scala.concurrent.duration._ +import scala.concurrent.ExecutionContext.Implicits.global + +trait AuraEffectBehavior { + _ : Actor => + private var activeEffectIndex : Long = 0 + private var effectsToIds : mutable.HashMap[Aura.Value, List[Long]] = mutable.HashMap.empty[Aura.Value, List[Long]] + private var idsToTimers : mutable.LongMap[Cancellable] = mutable.LongMap.empty[Cancellable] + private var idsToEntries : mutable.LongMap[AuraEffectBehavior.Entry] = mutable.LongMap.empty[AuraEffectBehavior.Entry] + + def AuraTargetObject : Player + + val auraBehavior : Receive = { + case AuraEffectBehavior.Aggravate(id, 0) => + CleanupEffect(id) match { + case Aura.None => ; + case _ => UpdateAggravatedEffect(AuraTargetObject) + } + + case AuraEffectBehavior.Aggravate(id, iteration) => ; + idsToTimers.remove(id) match { + case Some(timer) => timer.cancel + case _ => ; + } + idsToEntries.get(id) match { + case Some(entry) => + //TODO stuff ... + idsToTimers += id -> context.system.scheduler.scheduleOnce( + entry.effect.infliction_rate milliseconds, self, AuraEffectBehavior.Aggravate(id, iteration - 1)) + case _ => ; + } + } + + def AggravationEffect(data : ResolvedProjectile) : Unit = { + data.projectile.profile.Aggravated match { + case Some(damage) + if data.projectile.profile.ProjectileDamageType == DamageType.Aggravated && damage.effect_type != Aura.None => + AggravationEffect(damage, data) + case _ => ; + } + } + + private def AggravationEffect(aggravation : AggravatedDamage, data : ResolvedProjectile) : Unit = { + val effect = aggravation.effect_type + val obj = AuraTargetObject + if(obj.Aura.contains(effect)) { //TODO cumulative? + SetupAggravationEntry(aggravation) + } + else if(obj.Aura.diff(obj.AddEffectToAura(effect)).contains(effect)) { + SetupAggravationEntry(aggravation) + UpdateAggravatedEffect(obj) + } + } + + private def SetupAggravationEntry(aggravation : AggravatedDamage) : Unit = { + val effect = aggravation.effect_type + aggravation.info.foreach { infos => + //get unused id + val id = activeEffectIndex + activeEffectIndex += 1 + //pair aura effect with id + effectsToIds.get(effect) match { + case None | Some(Nil) => effectsToIds += effect -> List(id) + case Some(list) => effectsToIds -> (list :+ id) + } + //pair id with entry + idsToEntries += id -> AuraEffectBehavior.Entry(id, infos, aggravation, 0) + //pair id with timer + val iterations = (aggravation.duration / infos.infliction_rate).toInt + idsToTimers += id -> context.system.scheduler.scheduleOnce(infos.infliction_rate milliseconds, self, AuraEffectBehavior.Aggravate(id, iterations)) + } + } + + def CleanupEffect(id : Long) : Aura.Value = { + //remove and cancel timer + idsToTimers.remove(id) match { + case Some(timer) => timer.cancel + case _ => ; + } + //remove entry and cache effect + val out = idsToEntries.remove(id) match { + case Some(entry) => entry.aggravation.effect_type + case _ => Aura.None + } + //remove id and, if now unsupported, effect + (effectsToIds.get(out) match { + case Some(list) => (out, list.filterNot(_ == id)) + case _ => (Aura.None, Nil) + }) match { + case (Aura.None, _) => + Aura.None + case (effect, Nil) => + effectsToIds.remove(effect) + effect + case (effect, list) => + effectsToIds += effect -> list + Aura.None + } + } + + def EndAllEffects() : Unit = { + idsToEntries.clear + idsToTimers.values.foreach { _.cancel } + idsToTimers.clear + effectsToIds.clear + UpdateAggravatedEffect(AuraTargetObject) + } + + def UpdateAggravatedEffect(target : Player) : Unit = { + import services.avatar.{AvatarAction, AvatarServiceMessage} + val zone = target.Zone + val value = target.Aura.foldLeft(0)(_ + _.id) + zone.AvatarEvents ! AvatarServiceMessage(zone.Id, AvatarAction.PlanetsideAttributeToAll(target.GUID, 54, value)) + } +} + +object AuraEffectBehavior { + private case class Entry(id : Long, effect : AggravatedInfo, aggravation : AggravatedDamage, damage : Any) + + private case class Aggravate(id : Long, iteration : Int) +} diff --git a/common/src/main/scala/net/psforever/objects/ballistics/AggravatedDamage.scala b/common/src/main/scala/net/psforever/objects/ballistics/AggravatedDamage.scala index 67a217cc..e430bcda 100644 --- a/common/src/main/scala/net/psforever/objects/ballistics/AggravatedDamage.scala +++ b/common/src/main/scala/net/psforever/objects/ballistics/AggravatedDamage.scala @@ -1,13 +1,9 @@ // Copyright (c) 2017 PSForever package net.psforever.objects.ballistics +import net.psforever.objects.avatar.Aura import net.psforever.objects.vital.DamageType -object AggravatedEffect extends Enumeration { - type Type = Value - val Comet, Fire, Plasma, Napalm, None = Value -} - final case class AggravatedInfo(damage_type : DamageType.Value, degradation_percentage : Float, infliction_rate : Long) { @@ -15,7 +11,7 @@ final case class AggravatedInfo(damage_type : DamageType.Value, } final case class AggravatedDamage(info : List[AggravatedInfo], - effect_type : AggravatedEffect.Value, + effect_type : Aura.Value, duration : Long, max_factor : Float, cumulative_damage_degrade : Boolean, @@ -23,13 +19,13 @@ final case class AggravatedDamage(info : List[AggravatedInfo], object AggravatedDamage { def apply(info : AggravatedInfo, - effect_type : AggravatedEffect.Value, + effect_type : Aura.Value, duration : Long, max_factor : Float) : AggravatedDamage = AggravatedDamage(List(info), effect_type, duration, max_factor, cumulative_damage_degrade = true, vanu_aggravated = false) def apply(info : AggravatedInfo, - effect_type : AggravatedEffect.Value, + effect_type : Aura.Value, duration : Long, max_factor : Float, vanu_aggravated : Boolean) : AggravatedDamage = diff --git a/src/main/scala/net/psforever/actors/session/SessionActor.scala b/src/main/scala/net/psforever/actors/session/SessionActor.scala index 0c1c76b4..8770d1e4 100644 --- a/src/main/scala/net/psforever/actors/session/SessionActor.scala +++ b/src/main/scala/net/psforever/actors/session/SessionActor.scala @@ -3383,10 +3383,10 @@ class SessionActor extends Actor with MDCContextAware { context.system.scheduler.scheduleWithFixedDelay(0 seconds, 500 milliseconds, self, PokeClient()) accountIntermediary ! RetrieveAccountData(token) - case msg @ MountVehicleCargoMsg(player_guid, cargo_guid, carrier_guid, unk4) => + case msg@MountVehicleCargoMsg(player_guid, cargo_guid, carrier_guid, unk4) => log.info(msg.toString) (continent.GUID(cargo_guid), continent.GUID(carrier_guid)) match { - case (Some(cargo: Vehicle), Some(carrier: Vehicle)) => + case (Some(cargo : Vehicle), Some(carrier : Vehicle)) => carrier.CargoHolds.find({ case (_, hold) => !hold.isOccupied }) match { case Some((mountPoint, _)) => //try begin the mount process cargo.Actor ! CargoBehavior.CheckCargoMounting(carrier_guid, mountPoint, 0) @@ -3402,24 +3402,24 @@ class SessionActor extends Actor with MDCContextAware { case _ => ; } - case msg @ DismountVehicleCargoMsg(player_guid, cargo_guid, bailed, requestedByPassenger, kicked) => + case msg@DismountVehicleCargoMsg(player_guid, cargo_guid, bailed, requestedByPassenger, kicked) => log.info(msg.toString) //when kicked by carrier driver, player_guid will be PlanetSideGUID(0) //when exiting of the cargo vehicle driver's own accord, player_guid will be the cargo vehicle driver continent.GUID(cargo_guid) match { - case Some(cargo: Vehicle) if !requestedByPassenger => + case Some(cargo : Vehicle) if !requestedByPassenger => continent.GUID(cargo.MountedIn) match { - case Some(carrier: Vehicle) => + case Some(carrier : Vehicle) => CargoBehavior.HandleVehicleCargoDismount(continent, cargo_guid, bailed, requestedByPassenger, kicked) case _ => ; } case _ => ; } - case msg @ CharacterCreateRequestMessage(name, head, voice, gender, empire) => + case msg@CharacterCreateRequestMessage(name, head, voice, gender, empire) => avatarActor ! AvatarActor.CreateAvatar(name, head, voice, gender, empire) - case msg @ CharacterRequestMessage(charId, action) => + case msg@CharacterRequestMessage(charId, action) => action match { case CharacterRequestAction.Delete => avatarActor ! AvatarActor.DeleteAvatar(charId.toInt) @@ -3430,11 +3430,11 @@ class SessionActor extends Actor with MDCContextAware { case KeepAliveMessage(_) => keepAliveFunc() - case msg @ BeginZoningMessage() => + case msg@BeginZoningMessage() => log.info("Reticulating splines ...") zoneLoaded = None - val continentId = continent.id - val faction = player.Faction + val continentId = continent.id + val faction = player.Faction val factionChannel = s"$faction" continent.AvatarEvents ! Service.Join(continentId) continent.AvatarEvents ! Service.Join(factionChannel) @@ -3447,7 +3447,7 @@ class SessionActor extends Actor with MDCContextAware { if (connectionState != 100) configZone(continent) sendResponse(TimeOfDayMessage(1191182336)) //custom - sendResponse(ReplicationStreamMessage(5, Some(6), Vector.empty)) //clear squad list + sendResponse(ReplicationStreamMessage(5, Some(6), Vector.empty)) //clear squad list sendResponse(PlanetsideAttributeMessage(PlanetSideGUID(0), 112, 0)) // disable festive backpacks //find and reclaim own deployables, if any @@ -3476,7 +3476,7 @@ class SessionActor extends Actor with MDCContextAware { ) }) turrets.foreach(obj => { - val objGUID = obj.GUID + val objGUID = obj.GUID val definition = obj.Definition sendResponse( ObjectCreateMessage( @@ -3510,11 +3510,11 @@ class SessionActor extends Actor with MDCContextAware { normal .filter(obj => obj.Definition.DeployCategory == DeployableCategory.Sensors && - !obj.Destroyed && - (obj match { - case jObj: JammableUnit => !jObj.Jammed; - case _ => true - }) + !obj.Destroyed && + (obj match { + case jObj : JammableUnit => !jObj.Jammed; + case _ => true + }) ) .foreach(obj => { sendResponse(TriggerEffectMessage(obj.GUID, "on", true, 1000)) @@ -3581,7 +3581,7 @@ class SessionActor extends Actor with MDCContextAware { ( a, (continent.GUID(player.VehicleSeated) match { - case Some(vehicle: Vehicle) if vehicle.PassengerInSeat(player).isDefined => + case Some(vehicle : Vehicle) if vehicle.PassengerInSeat(player).isDefined => b.partition { _.GUID != vehicle.GUID } @@ -3599,7 +3599,7 @@ class SessionActor extends Actor with MDCContextAware { val allActiveVehicles = vehicles ++ usedVehicle //active vehicles (and some wreckage) vehicles.foreach(vehicle => { - val vguid = vehicle.GUID + val vguid = vehicle.GUID val vdefinition = vehicle.Definition sendResponse( ObjectCreateMessage(vdefinition.ObjectId, vguid, vdefinition.Packet.ConstructorData(vehicle).get) @@ -3609,7 +3609,7 @@ class SessionActor extends Actor with MDCContextAware { .filter({ case (index, seat) => seat.isOccupied && live.contains(seat.Occupant.get) && index > 0 }) .foreach({ case (index, seat) => - val targetPlayer = seat.Occupant.get + val targetPlayer = seat.Occupant.get val targetDefiniton = targetPlayer.avatar.definition sendResponse( ObjectCreateMessage( @@ -3637,7 +3637,7 @@ class SessionActor extends Actor with MDCContextAware { }) .foreach({ case (index, seat) => - val targetPlayer = seat.Occupant.get + val targetPlayer = seat.Occupant.get val targetDefinition = targetPlayer.avatar.definition sendResponse( ObjectCreateMessage( @@ -3691,9 +3691,9 @@ class SessionActor extends Actor with MDCContextAware { //special effects sendResponse(PlanetsideAttributeMessage(obj.GUID, 52, 1)) // ant panel glow Vehicles.FindANTChargingSource(obj, None).orElse(Vehicles.FindANTDischargingTarget(obj, None)) match { - case Some(silo: ResourceSilo) => + case Some(silo : ResourceSilo) => sendResponse(PlanetsideAttributeMessage(silo.GUID, 49, 1)) // silo orb particle effect - case Some(_: WarpGate) => + case Some(_ : WarpGate) => sendResponse(PlanetsideAttributeMessage(obj.GUID, 49, 1)) // ant orb particle effect case _ => ; } @@ -3710,7 +3710,7 @@ class SessionActor extends Actor with MDCContextAware { case ((terminal_guid, interface_guid)) => val parent_guid = PlanetSideGUID(terminal_guid) continent.GUID(interface_guid) match { - case Some(obj: Terminal) => + case Some(obj : Terminal) => val objDef = obj.Definition sendResponse( ObjectCreateMessage( @@ -3724,7 +3724,7 @@ class SessionActor extends Actor with MDCContextAware { } //seat terminal occupants continent.GUID(terminal_guid) match { - case Some(obj: Mountable) => + case Some(obj : Mountable) => obj.Seats(0).Occupant match { case Some(targetPlayer) => val targetDefinition = targetPlayer.avatar.definition @@ -3746,12 +3746,12 @@ class SessionActor extends Actor with MDCContextAware { continent.map.turretToWeapon .map { case ((turret_guid, _)) => continent.GUID(turret_guid) } .collect { - case Some(turret: FacilityTurret) => + case Some(turret : FacilityTurret) => val pguid = turret.GUID //attached weapon if (!turret.isUpgrading) { turret.ControlledWeapon(wepNumber = 1) match { - case Some(obj: Tool) => + case Some(obj : Tool) => val objDef = obj.Definition sendResponse( ObjectCreateMessage( @@ -3781,30 +3781,36 @@ class SessionActor extends Actor with MDCContextAware { case None => ; } } - continent.VehicleEvents ! VehicleServiceMessage(continent.id, VehicleAction.UpdateAmsSpawnPoint(continent)) + continent.VehicleEvents ! VehicleServiceMessage( + continent.Id, + VehicleAction.UpdateAmsSpawnPoint(continent) + ) upstreamMessageCount = 0 zoneLoaded = Some(true) case msg @ PlayerStateMessageUpstream( - avatar_guid, - pos, - vel, - yaw, - pitch, - yaw_upper, - seq_time, - unk3, - is_crouching, - is_jumping, - jump_thrust, - is_cloaking, - unk5, - unk6 - ) => + avatar_guid, + pos, + vel, + yaw, + pitch, + yaw_upper, + seq_time, + unk3, + is_crouching, + is_jumping, + jump_thrust, + is_cloaking, + unk5, + unk6 + ) => //log.info(s"$msg") persist() turnCounterFunc(avatar_guid) - val isMoving = WorldEntity.isMoving(vel) + if(is_crouching && !player.Crouching) { + //sendResponse(PlanetsideAttributeMessage(player.GUID, 54, 2)) + } + val isMoving = WorldEntity.isMoving(vel) val isMovingPlus = isMoving || is_jumping || jump_thrust if (isMovingPlus) { CancelZoningProcessWithDescriptiveReason("cancel_motion") @@ -3829,7 +3835,10 @@ class SessionActor extends Actor with MDCContextAware { } accessedContainer match { case Some(veh: Vehicle) => - if (isMoving || veh.isMoving(1) || Vector3.DistanceSquared(player.Position, veh.TrunkLocation) > 9) { + if (isMoving || veh.isMoving(1) || Vector3.DistanceSquared( + player.Position, + veh.TrunkLocation + ) > 9) { val guid = player.GUID sendResponse(UnuseItemMessage(guid, veh.GUID)) sendResponse(UnuseItemMessage(guid, guid)) diff --git a/src/main/scala/net/psforever/objects/GlobalDefinitions.scala b/src/main/scala/net/psforever/objects/GlobalDefinitions.scala index 561484b6..c581a8a5 100644 --- a/src/main/scala/net/psforever/objects/GlobalDefinitions.scala +++ b/src/main/scala/net/psforever/objects/GlobalDefinitions.scala @@ -2,7 +2,8 @@ package net.psforever.objects import net.psforever.objects.avatar.Certification -import net.psforever.objects.ballistics.{AggravatedDamage, AggravatedEffect, AggravatedInfo, Projectiles} +import net.psforever.objects.avatar.Aura +import net.psforever.objects.ballistics.{AggravatedDamage, AggravatedInfo, Projectiles} import net.psforever.objects.ce.{DeployableCategory, DeployedItem} import net.psforever.objects.definition._ import net.psforever.objects.definition.converter._ @@ -2318,7 +2319,7 @@ object GlobalDefinitions { aphelion_starfire_projectile.ProjectileDamageType = DamageType.Aggravated aphelion_starfire_projectile.Aggravated = AggravatedDamage( AggravatedInfo(DamageType.Direct, 0.25f, 250), - AggravatedEffect.None, + Aura.None, 0, 0f, true @@ -2449,7 +2450,7 @@ object GlobalDefinitions { comet_projectile.ProjectileDamageType = DamageType.Aggravated comet_projectile.Aggravated = AggravatedDamage( AggravatedInfo(DamageType.Direct, 0.2f, 500), - AggravatedEffect.Comet, + Aura.Comet, 0, 10f ) @@ -2600,7 +2601,7 @@ object GlobalDefinitions { flamethrower_fireball.ProjectileDamageType = DamageType.Aggravated flamethrower_fireball.Aggravated = AggravatedDamage( List(AggravatedInfo(DamageType.Direct, 0.9f, 500), AggravatedInfo(DamageType.Splash, 0.9f, 500)), - AggravatedEffect.Fire, + Aura.Fire, 5000, 0.1f, false, @@ -2621,7 +2622,7 @@ object GlobalDefinitions { flamethrower_projectile.ProjectileDamageType = DamageType.Aggravated flamethrower_projectile.Aggravated = AggravatedDamage( List(AggravatedInfo(DamageType.Direct, 0.5f, 500)), - AggravatedEffect.Fire, + Aura.Fire, 5000, 0.5f, false, @@ -3457,7 +3458,7 @@ object GlobalDefinitions { plasma_cartridge_projectile.ProjectileDamageType = DamageType.Aggravated plasma_cartridge_projectile.Aggravated = AggravatedDamage( List(AggravatedInfo(DamageType.Direct, 0.25f, 750), AggravatedInfo(DamageType.Splash, 0.25f, 1000)), - AggravatedEffect.Plasma, + Aura.Plasma, 0, 1.5f, true, @@ -3476,7 +3477,7 @@ object GlobalDefinitions { plasma_cartridge_projectile_b.ProjectileDamageType = DamageType.Aggravated plasma_cartridge_projectile_b.Aggravated = AggravatedDamage( List(AggravatedInfo(DamageType.Direct, 0.25f, 750), AggravatedInfo(DamageType.Splash, 0.25f, 1000)), - AggravatedEffect.Plasma, + Aura.Plasma, 0, 1.5f, true, @@ -3494,7 +3495,7 @@ object GlobalDefinitions { plasma_grenade_projectile.ProjectileDamageType = DamageType.Aggravated plasma_grenade_projectile.Aggravated = AggravatedDamage( List(AggravatedInfo(DamageType.Direct, 0.25f, 750), AggravatedInfo(DamageType.Splash, 0.25f, 1000)), - AggravatedEffect.Plasma, + Aura.Plasma, 0, 1.5f, true, @@ -3513,7 +3514,7 @@ object GlobalDefinitions { plasma_grenade_projectile_B.ProjectileDamageType = DamageType.Aggravated plasma_grenade_projectile_B.Aggravated = AggravatedDamage( List(AggravatedInfo(DamageType.Direct, 0.25f, 750), AggravatedInfo(DamageType.Splash, 0.25f, 1000)), - AggravatedEffect.Plasma, + Aura.Plasma, 0, 1.5f, true, @@ -3822,7 +3823,7 @@ object GlobalDefinitions { starfire_projectile.ProjectileDamageType = DamageType.Aggravated starfire_projectile.Aggravated = AggravatedDamage( AggravatedInfo(DamageType.Direct, 0.25f, 250), - AggravatedEffect.Comet, + Aura.Comet, 0, 0f, true diff --git a/src/main/scala/net/psforever/objects/Player.scala b/src/main/scala/net/psforever/objects/Player.scala index bd79347f..19ff2ad7 100644 --- a/src/main/scala/net/psforever/objects/Player.scala +++ b/src/main/scala/net/psforever/objects/Player.scala @@ -1,8 +1,16 @@ // Copyright (c) 2017 PSForever package net.psforever.objects -import net.psforever.objects.avatar.{Avatar, LoadoutManager} -import net.psforever.objects.definition.{AvatarDefinition, ExoSuitDefinition, SpecialExoSuitDefinition} +import net.psforever.objects.avatar.{ + Avatar, + Aura => AuraEffect, + LoadoutManager +} +import net.psforever.objects.definition.{ + AvatarDefinition, + ExoSuitDefinition, + SpecialExoSuitDefinition +} import net.psforever.objects.equipment.{Equipment, EquipmentSize, EquipmentSlot, JammableUnit} import net.psforever.objects.inventory.{Container, GridInventory, InventoryItem} import net.psforever.objects.serverobject.PlanetSideServerObject @@ -49,6 +57,8 @@ class Player(var avatar: Avatar) private var vehicleSeated: Option[PlanetSideGUID] = None + private var aura : Set[AuraEffect.Value] = Set.empty[AuraEffect.Value] + Continent = "home2" //the zone id var spectator: Boolean = false @@ -345,6 +355,18 @@ class Player(var avatar: Avatar) afk = away AwayFromKeyboard } + + def Aura : Set[AuraEffect.Value] = aura + + def AddEffectToAura(effect : AuraEffect.Value) : Set[AuraEffect.Value] = { + aura = aura + effect + Aura + } + + def RemoveEffectFromAura(effect : AuraEffect.Value) : Set[AuraEffect.Value] = { + aura = aura - effect + Aura + } private var usingSpecial: SpecialExoSuitDefinition.Mode.Value => SpecialExoSuitDefinition.Mode.Value = DefaultUsingSpecial