diff --git a/server/src/main/resources/overrides/game_objects0.adb.lst b/server/src/main/resources/overrides/game_objects0.adb.lst index be23a5ae4..5e08278e5 100644 --- a/server/src/main/resources/overrides/game_objects0.adb.lst +++ b/server/src/main/resources/overrides/game_objects0.adb.lst @@ -76,6 +76,7 @@ add_property pulsar equiptime 600 add_property pulsar holstertime 600 add_property punisher equiptime 600 add_property punisher holstertime 600 +add_property radiator allowed false add_property r_shotgun equiptime 750 add_property r_shotgun holstertime 750 add_property remote_electronics_kit equiptime 500 diff --git a/src/main/scala/net/psforever/actors/session/normal/GeneralLogic.scala b/src/main/scala/net/psforever/actors/session/normal/GeneralLogic.scala index 0a26acee8..1eb457f9d 100644 --- a/src/main/scala/net/psforever/actors/session/normal/GeneralLogic.scala +++ b/src/main/scala/net/psforever/actors/session/normal/GeneralLogic.scala @@ -41,7 +41,7 @@ import net.psforever.objects.zones.blockmap.BlockMapEntity import net.psforever.objects.zones.{Zone, ZoneProjectile, Zoning} import net.psforever.packet.PlanetSideGamePacket import net.psforever.packet.game.objectcreate.ObjectClass -import net.psforever.packet.game.{ActionCancelMessage, ActionResultMessage, AvatarFirstTimeEventMessage, AvatarImplantMessage, AvatarJumpMessage, BattleplanMessage, BindPlayerMessage, BindStatus, BugReportMessage, ChangeFireModeMessage, ChangeShortcutBankMessage, CharacterCreateRequestMessage, CharacterRequestAction, CharacterRequestMessage, ChatMsg, CollisionIs, ConnectToWorldRequestMessage, CreateShortcutMessage, DeadState, DeployObjectMessage, DisplayedAwardMessage, DropItemMessage, EmoteMsg, FacilityBenefitShieldChargeRequestMessage, FriendsRequest, GenericAction, GenericActionMessage, GenericCollisionMsg, GenericObjectActionAtPositionMessage, GenericObjectActionMessage, GenericObjectStateMsg, HitHint, ImplantAction, InvalidTerrainMessage, ItemTransactionMessage, LootItemMessage, MoveItemMessage, ObjectDeleteMessage, ObjectDetectedMessage, ObjectHeldMessage, PickupItemMessage, PlanetsideAttributeMessage, PlayerStateMessageUpstream, PlayerStateShiftMessage, RequestDestroyMessage, ShiftState, Shortcut, TargetInfo, TargetingImplantRequest, TargetingInfoMessage, TerrainCondition, TradeMessage, UnuseItemMessage, UseItemMessage, VoiceHostInfo, VoiceHostRequest, ZipLineMessage} +import net.psforever.packet.game.{ActionCancelMessage, ActionResultMessage, AvatarFirstTimeEventMessage, AvatarImplantMessage, AvatarJumpMessage, BattleplanMessage, BindPlayerMessage, BindStatus, BugReportMessage, ChangeFireModeMessage, ChangeShortcutBankMessage, CharacterCreateRequestMessage, CharacterRequestAction, CharacterRequestMessage, ChatMsg, CollisionIs, ConnectToWorldRequestMessage, CreateShortcutMessage, DeadState, DeployObjectMessage, DisplayedAwardMessage, DropItemMessage, EmoteMsg, FacilityBenefitShieldChargeRequestMessage, FriendsRequest, GenericAction, GenericActionMessage, GenericCollisionMsg, GenericObjectActionAtPositionMessage, GenericObjectActionMessage, GenericObjectStateMsg, HitHint, ImplantAction, InvalidTerrainMessage, ItemTransactionMessage, LootItemMessage, MoveItemMessage, ObjectDeleteMessage, ObjectDetectedMessage, ObjectHeldMessage, PickupItemMessage, PlanetsideAttributeMessage, PlayerStateMessageUpstream, PlayerStateShiftMessage, RequestDestroyMessage, ShiftState, TargetInfo, TargetingImplantRequest, TargetingInfoMessage, TerrainCondition, TradeMessage, UnuseItemMessage, UseItemMessage, VoiceHostInfo, VoiceHostRequest, ZipLineMessage} import net.psforever.services.RemoverActor import net.psforever.services.account.{AccountPersistenceService, RetrieveAccountData} import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage} @@ -1025,13 +1025,18 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex private def handleUseResourceSilo(resourceSilo: ResourceSilo, equipment: Option[Equipment]): Unit = { sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_use") - (continent.GUID(player.VehicleSeated), equipment) match { + val vehicleOpt = continent.GUID(player.avatar.vehicle) + (vehicleOpt, equipment) match { case (Some(vehicle: Vehicle), Some(item)) if GlobalDefinitions.isBattleFrameVehicle(vehicle.Definition) && GlobalDefinitions.isBattleFrameNTUSiphon(item.Definition) => - resourceSilo.Actor ! CommonMessages.Use(player, equipment) - case _ => - resourceSilo.Actor ! CommonMessages.Use(player) + resourceSilo.Actor ! CommonMessages.Use(player, Some(vehicle)) + case (Some(vehicle: Vehicle), _) + if vehicle.Definition == GlobalDefinitions.ant && + vehicle.DeploymentState == DriveState.Deployed && + Vector3.DistanceSquared(resourceSilo.Position.xy, vehicle.Position.xy) < math.pow(resourceSilo.Definition.UseRadius, 2) => + resourceSilo.Actor ! CommonMessages.Use(player, Some(vehicle)) + case _ => () } } diff --git a/src/main/scala/net/psforever/actors/session/normal/LocalHandlerLogic.scala b/src/main/scala/net/psforever/actors/session/normal/LocalHandlerLogic.scala index 8ddb6896b..009d0bbd7 100644 --- a/src/main/scala/net/psforever/actors/session/normal/LocalHandlerLogic.scala +++ b/src/main/scala/net/psforever/actors/session/normal/LocalHandlerLogic.scala @@ -4,6 +4,7 @@ package net.psforever.actors.session.normal import akka.actor.ActorContext import net.psforever.actors.session.support.{LocalHandlerFunctions, SessionData, SessionLocalHandlers} import net.psforever.objects.ce.Deployable +import net.psforever.objects.serverobject.doors.Door import net.psforever.objects.vehicles.MountableWeapons import net.psforever.objects.{BoomerDeployable, ExplosiveDeployable, TelepadDeployable, Tool, TurretDeployable} import net.psforever.packet.game.{ChatMsg, DeployableObjectsInfoMessage, GenericActionMessage, GenericObjectActionMessage, GenericObjectStateMsg, HackMessage, HackState, InventoryStateMessage, ObjectAttachMessage, ObjectCreateMessage, ObjectDeleteMessage, ObjectDetachMessage, OrbitalShuttleTimeMsg, PadAndShuttlePair, PlanetsideAttributeMessage, ProximityTerminalUseMessage, SetEmpireMessage, TriggerEffectMessage, TriggerSoundMessage, TriggeredSound, VehicleStateMessage} @@ -66,7 +67,18 @@ class LocalHandlerLogic(val ops: SessionLocalHandlers, implicit val context: Act log.warn(s"LocalResponse.Detonate: ${obj.Definition.Name} not configured to explode correctly") case LocalResponse.DoorOpens(doorGuid) if isNotSameTarget => - sendResponse(GenericObjectStateMsg(doorGuid, state=16)) + val pos = player.Position.xy + val range = ops.doorLoadRange() + val foundDoor = continent + .blockMap + .sector(pos, range) + .amenityList + .collect { case door: Door => door } + .find(_.GUID == doorGuid) + val doorExistsInRange: Boolean = foundDoor.nonEmpty + if (doorExistsInRange) { + sendResponse(GenericObjectStateMsg(doorGuid, state=16)) + } case LocalResponse.DoorCloses(doorGuid) => //door closes for everyone sendResponse(GenericObjectStateMsg(doorGuid, state=17)) @@ -95,16 +107,14 @@ class LocalHandlerLogic(val ops: SessionLocalHandlers, implicit val context: Act case LocalResponse.EliminateDeployable(obj: TelepadDeployable, dguid, _, _) if obj.Active && obj.Destroyed => //if active, deactivate obj.Active = false - sendResponse(GenericObjectActionMessage(dguid, code=29)) - sendResponse(GenericObjectActionMessage(dguid, code=30)) + ops.deactivateTelpadDeployableMessages(dguid) //standard deployable elimination behavior sendResponse(ObjectDeleteMessage(dguid, unk1=0)) case LocalResponse.EliminateDeployable(obj: TelepadDeployable, dguid, pos, _) if obj.Active => //if active, deactivate obj.Active = false - sendResponse(GenericObjectActionMessage(dguid, code=29)) - sendResponse(GenericObjectActionMessage(dguid, code=30)) + ops.deactivateTelpadDeployableMessages(dguid) //standard deployable elimination behavior obj.Destroyed = true DeconstructDeployable(obj, dguid, pos, obj.Orientation, deletionType=2) diff --git a/src/main/scala/net/psforever/actors/session/normal/WeaponAndProjectileLogic.scala b/src/main/scala/net/psforever/actors/session/normal/WeaponAndProjectileLogic.scala index 05608922b..cc5e710b7 100644 --- a/src/main/scala/net/psforever/actors/session/normal/WeaponAndProjectileLogic.scala +++ b/src/main/scala/net/psforever/actors/session/normal/WeaponAndProjectileLogic.scala @@ -545,9 +545,9 @@ class WeaponAndProjectileLogic(val ops: WeaponAndProjectileOperations, implicit turret.Actor ! AutomatedTurretBehavior.ConfirmShot(target) Some(target) - case turret: AutomatedTurret => + case turret: AutomatedTurret with OwnableByPlayer => turret.Actor ! AutomatedTurretBehavior.ConfirmShot(target) - HandleAIDamage(target, CompileAutomatedTurretDamageData(turret, turret.TurretOwner, projectileTypeId)) + HandleAIDamage(target, CompileAutomatedTurretDamageData(turret, projectileTypeId)) Some(target) } } @@ -558,9 +558,9 @@ class WeaponAndProjectileLogic(val ops: WeaponAndProjectileOperations, implicit case target: PlanetSideServerObject with FactionAffinity with Vitality => sessionLogic.validObject(attackerGuid, decorator = "AIDamage/Attacker") .collect { - case turret: AutomatedTurret if turret.Target.nonEmpty => + case turret: AutomatedTurret with OwnableByPlayer if turret.Target.nonEmpty => //the turret must be shooting at something (else) first - HandleAIDamage(target, CompileAutomatedTurretDamageData(turret, turret.TurretOwner, projectileTypeId)) + HandleAIDamage(target, CompileAutomatedTurretDamageData(turret, projectileTypeId)) } Some(target) } @@ -1268,14 +1268,17 @@ class WeaponAndProjectileLogic(val ops: WeaponAndProjectileOperations, implicit } private def CompileAutomatedTurretDamageData( - turret: AutomatedTurret, - owner: SourceEntry, + turret: AutomatedTurret with OwnableByPlayer, projectileTypeId: Long ): Option[(AutomatedTurret, Tool, SourceEntry, ProjectileDefinition)] = { turret.Weapons .values .flatMap { _.Equipment } - .collect { case weapon: Tool => (turret, weapon, owner, weapon.Projectile) } + .collect { + case weapon: Tool => + val source = Deployables.AssignBlameTo(continent, turret.OwnerName, SourceEntry(turret)) + (turret, weapon, source, weapon.Projectile) + } .find { case (_, _, _, p) => p.ObjectId == projectileTypeId } } diff --git a/src/main/scala/net/psforever/actors/session/spectator/LocalHandlerLogic.scala b/src/main/scala/net/psforever/actors/session/spectator/LocalHandlerLogic.scala index 21d85d7a1..c0810ab64 100644 --- a/src/main/scala/net/psforever/actors/session/spectator/LocalHandlerLogic.scala +++ b/src/main/scala/net/psforever/actors/session/spectator/LocalHandlerLogic.scala @@ -96,16 +96,14 @@ class LocalHandlerLogic(val ops: SessionLocalHandlers, implicit val context: Act case LocalResponse.EliminateDeployable(obj: TelepadDeployable, dguid, _, _) if obj.Active && obj.Destroyed => //if active, deactivate obj.Active = false - sendResponse(GenericObjectActionMessage(dguid, code=29)) - sendResponse(GenericObjectActionMessage(dguid, code=30)) + ops.deactivateTelpadDeployableMessages(dguid) //standard deployable elimination behavior sendResponse(ObjectDeleteMessage(dguid, unk1=0)) case LocalResponse.EliminateDeployable(obj: TelepadDeployable, dguid, pos, _) if obj.Active => //if active, deactivate obj.Active = false - sendResponse(GenericObjectActionMessage(dguid, code=29)) - sendResponse(GenericObjectActionMessage(dguid, code=30)) + ops.deactivateTelpadDeployableMessages(dguid) //standard deployable elimination behavior obj.Destroyed = true DeconstructDeployable(obj, dguid, pos, obj.Orientation, deletionType=2) diff --git a/src/main/scala/net/psforever/actors/session/support/SessionLocalHandlers.scala b/src/main/scala/net/psforever/actors/session/support/SessionLocalHandlers.scala index 456e3a237..7fc6a893b 100644 --- a/src/main/scala/net/psforever/actors/session/support/SessionLocalHandlers.scala +++ b/src/main/scala/net/psforever/actors/session/support/SessionLocalHandlers.scala @@ -5,6 +5,8 @@ import akka.actor.ActorContext import net.psforever.objects.{Players, TurretDeployable} import net.psforever.objects.ce.Deployable import net.psforever.objects.guid.{GUIDTask, TaskWorkflow} +import net.psforever.objects.serverobject.interior.Sidedness +import net.psforever.packet.game.GenericObjectActionMessage import net.psforever.services.local.LocalResponse import net.psforever.types.PlanetSideGUID @@ -22,7 +24,10 @@ class SessionLocalHandlers( val sessionLogic: SessionData, implicit val context: ActorContext ) extends CommonSessionInterfacingFunctionality { - + def deactivateTelpadDeployableMessages(guid: PlanetSideGUID): Unit = { + sendResponse(GenericObjectActionMessage(guid, code = 29)) + sendResponse(GenericObjectActionMessage(guid, code = 30)) + } def handleTurretDeployableIsDismissed(obj: TurretDeployable): Unit = { Players.buildCooldownReset(continent, player.Name, obj) @@ -33,4 +38,13 @@ class SessionLocalHandlers( Players.buildCooldownReset(continent, player.Name, obj) TaskWorkflow.execute(GUIDTask.unregisterObject(continent.GUID, obj)) } + + def doorLoadRange(): Float = { + if (Sidedness.equals(player.WhichSide, Sidedness.InsideOf)) + 100f + else if (sessionLogic.general.canSeeReallyFar) + 800f + else + 400f + } } diff --git a/src/main/scala/net/psforever/actors/session/support/ZoningOperations.scala b/src/main/scala/net/psforever/actors/session/support/ZoningOperations.scala index 5981f3850..6648cf3ba 100644 --- a/src/main/scala/net/psforever/actors/session/support/ZoningOperations.scala +++ b/src/main/scala/net/psforever/actors/session/support/ZoningOperations.scala @@ -2227,15 +2227,28 @@ class ZoningOperations( sessionLogic.actionsToCancel() continent.GUID(player.VehicleSeated) match { case Some(vehicle: Vehicle) if vehicle.MountedIn.isEmpty => - vehicle.PassengerInSeat(player) match { - case Some(0) => - deadState = DeadState.Release // cancel movement updates - vehicle.Position = position - LoadZonePhysicalSpawnPoint(continent.id, position, Vector3.z(vehicle.Orientation.z), 0 seconds, None) - case _ => // not seated as the driver, in which case we can't move + vehicle + .PassengerInSeat(player) + .collect { + case 0 => //driver of the vehicle carries the vehicle and its passengers + deadState = DeadState.Release //cancel movement updates + vehicle.Position = position + doorsThatShouldBeClosedOrBeOpenedByRange( + player.Position, + sessionLogic.localResponse.doorLoadRange(), + position, + openRange = 100f + ) + LoadZonePhysicalSpawnPoint(continent.id, position, Vector3.z(vehicle.Orientation.z), 0 seconds, None) } case None => deadState = DeadState.Release // cancel movement updates + doorsThatShouldBeClosedOrBeOpenedByRange( + player.Position, + sessionLogic.localResponse.doorLoadRange(), + position, + openRange = 100f + ) player.Position = position sendResponse(PlayerStateShiftMessage(ShiftState(0, position, player.Orientation.z, None))) deadState = DeadState.Alive // must be set here @@ -3105,6 +3118,7 @@ class ZoningOperations( } }) } + doorsThatShouldBeOpenInRange(pos, range = 100f) setAvatar = true player.allowInteraction = true upstreamMessageCount = 0 @@ -3622,6 +3636,32 @@ class ZoningOperations( } } + def doorsThatShouldBeClosedOrBeOpenedByRange( + closedPosition: Vector3, + closedRange: Float, + openPosition: Vector3, + openRange: Float + ): Unit = { + continent + .blockMap + .sector(closedPosition, closedRange) + .amenityList + .collect { case door: Door if door.isOpen => + sendResponse(GenericObjectStateMsg(door.GUID, state=17)) + } + doorsThatShouldBeOpenInRange(openPosition, openRange) + } + + def doorsThatShouldBeOpenInRange(position: Vector3, range: Float): Unit = { + continent + .blockMap + .sector(position.xy, range) + .amenityList + .collect { case door: Door if door.isOpen => + sendResponse(GenericObjectStateMsg(door.GUID, state=16)) + } + } + override protected[session] def stop(): Unit = { zoningTimer.cancel() spawn.respawnTimer.cancel() diff --git a/src/main/scala/net/psforever/objects/Deployables.scala b/src/main/scala/net/psforever/objects/Deployables.scala index 826b38761..c75057197 100644 --- a/src/main/scala/net/psforever/objects/Deployables.scala +++ b/src/main/scala/net/psforever/objects/Deployables.scala @@ -6,6 +6,7 @@ import net.psforever.objects.avatar.{Avatar, Certification} import scala.concurrent.duration._ import net.psforever.objects.ce.{Deployable, DeployedItem} +import net.psforever.objects.sourcing.{PlayerSource, SourceEntry} import net.psforever.objects.zones.Zone import net.psforever.packet.game._ import net.psforever.types.PlanetSideGUID @@ -261,4 +262,48 @@ object Deployables { } (sample intersect testDiff equals testDiff) && (sampleIntersect intersect testIntersect equals testIntersect) } + + /** + * Find a player with a given name in this zone. + * The assumption is the player is the owner of a given deployable entity. + * If the player can not be found, the deployable entity can stand in as it's own owner. + * @param zone continent in which the player should be found; + * should be the same zone as the deployable, but not required + * @param nameOpt optional player's name + * @param deployableSource deployable entity + * @return discovered player as a reference + */ + def AssignBlameTo(zone: Zone, nameOpt: Option[String], deployableSource: SourceEntry): SourceEntry = { + zone + .Players + .find(a => nameOpt.contains(a.name)) + .collect { a => + val name = a.name + Deployables.AssignBlameToFrom(name, zone.LivePlayers) + .orElse(Deployables.AssignBlameToFrom(name, zone.Corpses)) + .getOrElse { + val player = PlayerSource(name, deployableSource.Faction, deployableSource.Position) //might report minor inconsistencies, e.g., exo-suit type + player.copy(unique = player.unique.copy(charId = a.id), progress = a.scorecard.CurrentLife) + } + } + .getOrElse(deployableSource) + } + + /** + * Find a player with a given name from this list of possible players. + * If the player is seated, attach a shallow copy of the mounting information. + * @param name player name + * @param blameList possible players in which to find the player name + * @return discovered player as a reference, or `None` if not found + */ + private def AssignBlameToFrom(name: String, blameList: List[Player]): Option[SourceEntry] = { + blameList + .find(_.Name.equals(name)) + .map { player => + PlayerSource + .mountableAndSeat(player) + .map { case (mount, seat) => PlayerSource.inSeat(player, mount, seat) } + .getOrElse { PlayerSource(player) } + } + } } diff --git a/src/main/scala/net/psforever/objects/MineDeployableControl.scala b/src/main/scala/net/psforever/objects/MineDeployableControl.scala index 3b393378a..0a1e46461 100644 --- a/src/main/scala/net/psforever/objects/MineDeployableControl.scala +++ b/src/main/scala/net/psforever/objects/MineDeployableControl.scala @@ -5,7 +5,7 @@ import akka.actor.{ActorContext, ActorRef, Props} import net.psforever.objects.ce.{Deployable, DeployedItem} import net.psforever.objects.serverobject.PlanetSideServerObject import net.psforever.objects.serverobject.affinity.FactionAffinity -import net.psforever.objects.sourcing.{DeployableSource, PlayerSource, SourceEntry} +import net.psforever.objects.sourcing.{DeployableSource, SourceEntry} import net.psforever.objects.vital.Vitality import net.psforever.objects.vital.etc.TrippedMineReason import net.psforever.objects.vital.interaction.DamageInteraction @@ -98,40 +98,8 @@ object MineDeployableControl { private case class Triggered() def trippedMineReason(mine: ExplosiveDeployable): TrippedMineReason = { - lazy val deployableSource = DeployableSource(mine) - val zone = mine.Zone - val ownerName = mine.OwnerName - val blame = zone - .Players - .find(a => ownerName.contains(a.name)) - .collect { a => - val name = a.name - assignBlameToFrom(name, zone.LivePlayers) - .orElse(assignBlameToFrom(name, zone.Corpses)) - .getOrElse { - val player = PlayerSource(name, mine.Faction, mine.Position) //might report minor inconsistencies, e.g., exo-suit type - player.copy(unique = player.unique.copy(charId = a.id), progress = a.scorecard.CurrentLife) - } - } - .getOrElse(deployableSource) + val deployableSource = DeployableSource(mine) + val blame = Deployables.AssignBlameTo(mine.Zone, mine.OwnerName, deployableSource) TrippedMineReason(deployableSource, blame) } - - /** - * Find a player with a given name from this list of possible players. - * If the player is seated, attach a shallow copy of the mounting information. - * @param name player name - * @param blameList possible players in which to find the player name - * @return discovered player as a reference, or `None` if not found - */ - private def assignBlameToFrom(name: String, blameList: List[Player]): Option[SourceEntry] = { - blameList - .find(_.Name.equals(name)) - .map { player => - PlayerSource - .mountableAndSeat(player) - .map { case (mount, seat) => PlayerSource.inSeat(player, mount, seat) } - .getOrElse { PlayerSource(player) } - } - } } diff --git a/src/main/scala/net/psforever/objects/avatar/DeployableToolbox.scala b/src/main/scala/net/psforever/objects/avatar/DeployableToolbox.scala index 2a6f4d23b..29c310101 100644 --- a/src/main/scala/net/psforever/objects/avatar/DeployableToolbox.scala +++ b/src/main/scala/net/psforever/objects/avatar/DeployableToolbox.scala @@ -22,9 +22,9 @@ import scala.collection.mutable * As deployables are added and removed, and tracked certifications are added and removed, * these structures are updated to reflect proper count. * For example, the greatest number of spitfire turrets that can be placed is 15 (individual count) - * and the greatest number of shadow turrets and cerebus turrets that can be placed is 5 each (individual counts) + * and the greatest number of shadow turrets and cerberus turrets that can be placed is 5 each (individual counts) * but the maximum number of small turrets that can be placed overall is only 15 (categorical count). - * Spitfire turrets, shadow turrets, and cerebus turrets are all included in the category of small turrets. + * Spitfire turrets, shadow turrets, and cerberus turrets are all included in the category of small turrets. */ class DeployableToolbox { diff --git a/src/main/scala/net/psforever/objects/ce/Deployable.scala b/src/main/scala/net/psforever/objects/ce/Deployable.scala index a77989c20..ed2114172 100644 --- a/src/main/scala/net/psforever/objects/ce/Deployable.scala +++ b/src/main/scala/net/psforever/objects/ce/Deployable.scala @@ -114,7 +114,7 @@ object Deployable { DeployedItem.jammer_mine.id -> DeployableIcon.DisruptorMine, DeployedItem.spitfire_turret.id -> DeployableIcon.SpitfireTurret, DeployedItem.spitfire_cloaked.id -> DeployableIcon.ShadowTurret, - DeployedItem.spitfire_aa.id -> DeployableIcon.CerebusTurret, + DeployedItem.spitfire_aa.id -> DeployableIcon.cerberusTurret, DeployedItem.motionalarmsensor.id -> DeployableIcon.MotionAlarmSensor, DeployedItem.sensor_shield.id -> DeployableIcon.SensorDisruptor, DeployedItem.tank_traps.id -> DeployableIcon.TRAP, diff --git a/src/main/scala/net/psforever/objects/ce/DeployedItem.scala b/src/main/scala/net/psforever/objects/ce/DeployedItem.scala index ff6d5e1e3..c144c2be9 100644 --- a/src/main/scala/net/psforever/objects/ce/DeployedItem.scala +++ b/src/main/scala/net/psforever/objects/ce/DeployedItem.scala @@ -10,7 +10,7 @@ object DeployedItem extends Enumeration { final val jammer_mine = Value(420) //disruptor mine final val motionalarmsensor = Value(575) final val sensor_shield = Value(752) //sensor disruptor - final val spitfire_aa = Value(819) //cerebus turret + final val spitfire_aa = Value(819) //cerberus turret final val spitfire_cloaked = Value(825) //shadow turret final val spitfire_turret = Value(826) final val tank_traps = Value(849) //trap diff --git a/src/main/scala/net/psforever/objects/global/GlobalDefinitionsImplant.scala b/src/main/scala/net/psforever/objects/global/GlobalDefinitionsImplant.scala index a64080e17..347c99b90 100644 --- a/src/main/scala/net/psforever/objects/global/GlobalDefinitionsImplant.scala +++ b/src/main/scala/net/psforever/objects/global/GlobalDefinitionsImplant.scala @@ -18,6 +18,7 @@ object GlobalDefinitionsImplant { targeting.Name = "targeting" targeting.InitializationDuration = 60 + targeting.Passive = true audio_amplifier.Name = "audio_amplifier" audio_amplifier.InitializationDuration = 60 @@ -41,9 +42,11 @@ object GlobalDefinitionsImplant { range_magnifier.Name = "range_magnifier" range_magnifier.InitializationDuration = 60 + range_magnifier.Passive = true second_wind.Name = "second_wind" second_wind.InitializationDuration = 180 + second_wind.Passive = true silent_run.Name = "silent_run" silent_run.InitializationDuration = 90 diff --git a/src/main/scala/net/psforever/objects/global/GlobalDefinitionsMiscellaneous.scala b/src/main/scala/net/psforever/objects/global/GlobalDefinitionsMiscellaneous.scala index 12868e001..41b224adc 100644 --- a/src/main/scala/net/psforever/objects/global/GlobalDefinitionsMiscellaneous.scala +++ b/src/main/scala/net/psforever/objects/global/GlobalDefinitionsMiscellaneous.scala @@ -698,6 +698,7 @@ object GlobalDefinitionsMiscellaneous { spawn_tube_door_coffin.Damageable = true resource_silo.Name = "resource_silo" + resource_silo.UseRadius = 22 //20 resource_silo.Damageable = false resource_silo.Repairable = false resource_silo.MaxNtuCapacitor = 1000 diff --git a/src/main/scala/net/psforever/objects/guid/UniqueNumberOps.scala b/src/main/scala/net/psforever/objects/guid/UniqueNumberOps.scala index 279255dc7..e1a8a8210 100644 --- a/src/main/scala/net/psforever/objects/guid/UniqueNumberOps.scala +++ b/src/main/scala/net/psforever/objects/guid/UniqueNumberOps.scala @@ -50,7 +50,7 @@ class UniqueNumberOps( private val poolActors: Map[String, ActorRef] ) { /** The timeout used by all number pool `ask` messaging */ - private implicit val timeout = UniqueNumberOps.timeout + private implicit val timeout: Timeout = UniqueNumberOps.timeout /** * The entry point for the entity GUID registration process. @@ -149,25 +149,26 @@ class UniqueNumberOps( val localPool = pool val result = ask(pool, NumberPoolActor.GetAnyNumber())(timeout) - result.onComplete { - case Success(NumberPoolActor.GiveNumber(number)) => - UniqueNumberOps.processRegisterResult( - localPromise, - localTarget, - localUns, - localPoolName, - localPool, - number - ) - case Success(NumberPoolActor.NoNumber(ex)) => - registrationProcessRetry(localPromise, ex, localTarget, localUns, localPools, localPoolName) - case msg => - UniqueNumberOps.log.warn(s"unexpected message during $localTarget's registration process - $msg") - } - result.recover { - case ex: AskTimeoutException => - localPromise.failure(new RegisteringException(msg = s"did not register entity $localTarget in time", ex)) - } + result + .recover { + case ex: AskTimeoutException => + localPromise.failure(new RegisteringException(msg = s"did not register entity $localTarget in time", ex)) + } + .onComplete { + case Success(NumberPoolActor.GiveNumber(number)) => + UniqueNumberOps.processRegisterResult( + localPromise, + localTarget, + localUns, + localPoolName, + localPool, + number + ) + case Success(NumberPoolActor.NoNumber(ex)) => + registrationProcessRetry(localPromise, ex, localTarget, localUns, localPools, localPoolName) + case msg => + UniqueNumberOps.log.warn(s"unexpected message during $localTarget's registration process - $msg") + } case None => //do not log @@ -197,7 +198,7 @@ class UniqueNumberOps( if (poolName.equals("generic")) { promise.failure(new RegisteringException(msg = s"did not register entity $obj", exception)) } else { - org.log4s.getLogger("UniqueNumberOps").warn(s"${exception.getLocalizedMessage()} - $poolName") + UniqueNumberOps.log.warn(s"${exception.getLocalizedMessage()} - $poolName") promise.completeWith(registrationProcess(obj, guid, pools, poolName = "generic")) } } @@ -302,7 +303,7 @@ class UniqueNumberOps( object UniqueNumberOps { private val log = org.log4s.getLogger - private implicit val timeout = Timeout(2.seconds) + private implicit val timeout: Timeout = Timeout(4.seconds) /** * Final step of the object registration process. @@ -431,7 +432,7 @@ class UniqueNumberSetup( ) extends Actor { init() - final def receive: Receive = { case _ => ; } + final def receive: Receive = { case _ => () } def init(): UniqueNumberOps = { new UniqueNumberOps(hub, poolActorConversionFunc(context, hub)) diff --git a/src/main/scala/net/psforever/objects/serverobject/CommonMessages.scala b/src/main/scala/net/psforever/objects/serverobject/CommonMessages.scala index 7f19cf8d2..ef1d4679b 100644 --- a/src/main/scala/net/psforever/objects/serverobject/CommonMessages.scala +++ b/src/main/scala/net/psforever/objects/serverobject/CommonMessages.scala @@ -10,7 +10,8 @@ object CommonMessages { final case class Unuse(player: Player, data: Option[Any] = None) final case class Hack(player: Player, obj: PlanetSideServerObject with Hackable, data: Option[Any] = None) final case class ClearHack() - + final case class EntityHackState(obj: PlanetSideGameObject with Hackable, hackState: Boolean) + /** * The message that progresses some form of user-driven activity with a certain eventual outcome * and potential feedback per cycle. diff --git a/src/main/scala/net/psforever/objects/serverobject/deploy/Interference.scala b/src/main/scala/net/psforever/objects/serverobject/deploy/Interference.scala index 16ba67795..8b0cae70e 100644 --- a/src/main/scala/net/psforever/objects/serverobject/deploy/Interference.scala +++ b/src/main/scala/net/psforever/objects/serverobject/deploy/Interference.scala @@ -77,7 +77,7 @@ object Interference { val sector = zone.blockMap.sector(position, Interference.MaxRange) val targets = (sector.deployableList ++ sector.vehicleList.filter(_.DeploymentState >= DriveState.Deploying)) .collect { case target: PlanetSideGameObject with FactionAffinity - if target.Faction != faction && + if target.Faction == faction && (target.Definition.asInstanceOf[ObjectDefinition].interference ne Interference.AllowAll) => target } diff --git a/src/main/scala/net/psforever/objects/serverobject/hackable/GenericHackables.scala b/src/main/scala/net/psforever/objects/serverobject/hackable/GenericHackables.scala index ed83e5ce0..a88b1907e 100644 --- a/src/main/scala/net/psforever/objects/serverobject/hackable/GenericHackables.scala +++ b/src/main/scala/net/psforever/objects/serverobject/hackable/GenericHackables.scala @@ -31,12 +31,12 @@ object GenericHackables { turretUpgradeTime } /** - * na - * - * @param player the player doing the hacking - * @param obj the object being hacked - * @return the percentage amount of progress per tick - */ + * na + * + * @param player the player doing the hacking + * @param obj the object being hacked + * @return the percentage amount of progress per tick + */ def GetHackSpeed(player: Player, obj: PlanetSideServerObject): Float = { val playerHackLevel = player.avatar.hackingSkillLevel() val timeToHack = obj match { @@ -60,23 +60,23 @@ object GenericHackables { } /** - * Evaluate the progress of the user applying a tool to modify some server object. - * This action is using the remote electronics kit to convert an enemy unit into an allied unit, primarily. - * The act of transforming allied units of one kind into allied units of another kind (facility turret upgrades) - * is also governed by this action per tick of progress. - * @see `HackMessage` - * @see `HackState` - * @param progressType 1 - remote electronics kit hack (various ...); - * 2 - nano dispenser (upgrade canister) turret upgrade - * @param tplayer the player performing the action - * @param target the object being affected - * @param tool_guid the tool being used to affest the object - * @param progress the current progress value - * @return `true`, if the next cycle of progress should occur; - * `false`, otherwise - */ + * Evaluate the progress of the user applying a tool to modify some server object. + * This action is using the remote electronics kit to convert an enemy unit into an allied unit, primarily. + * The act of transforming allied units of one kind into allied units of another kind (facility turret upgrades) + * is also governed by this action per tick of progress. + * @see `HackMessage` + * @see `HackState` + * @param progressType 1 - remote electronics kit hack (various ...); + * 2 - nano dispenser (upgrade canister) turret upgrade + * @param tplayer the player performing the action + * @param target the object being affected + * @param tool_guid the tool being used to affest the object + * @param progress the current progress value + * @return `true`, if the next cycle of progress should occur; + * `false`, otherwise + */ def HackingTickAction(progressType: Int, tplayer: Player, target: PlanetSideServerObject, tool_guid: PlanetSideGUID)( - progress: Float + progress: Float ): Boolean = { //hack state for progress bar visibility val vis = if (progress <= 0L) { @@ -108,37 +108,40 @@ object GenericHackables { } /** - * The process of hacking an object is completed. - * Pass the message onto the hackable object and onto the local events system. - * @param target the `Hackable` object that has been hacked - * @param user the player that is performing this hacking task - * @param unk na; - * used by `HackMessage` as `unk5` - * @see `HackMessage` - */ + * The process of hacking an object is completed. + * Pass the message onto the hackable object and onto the local events system. + * @param target the `Hackable` object that has been hacked + * @param user the player that is performing this hacking task + * @param unk na; + * used by `HackMessage` as `unk5` + * @see `HackMessage` + */ //TODO add params here depending on which params in HackMessage are important def FinishHacking(target: PlanetSideServerObject with Hackable, user: Player, unk: Long)(): Unit = { import akka.pattern.ask import scala.concurrent.duration._ - log.info(s"${user.Name} hacked a ${target.Definition.Name}") // Wait for the target actor to set the HackedBy property, otherwise LocalAction.HackTemporarily will not complete properly import scala.concurrent.ExecutionContext.Implicits.global val tplayer = user - ask(target.Actor, CommonMessages.Hack(tplayer, target))(1 second).mapTo[Boolean].onComplete { - case Success(_) => - val zone = target.Zone - val zoneId = zone.id - val pguid = tplayer.GUID - zone.LocalEvents ! LocalServiceMessage( - zoneId, - LocalAction.TriggerSound(pguid, target.HackSound, tplayer.Position, 30, 0.49803925f) - ) - zone.LocalEvents ! LocalServiceMessage( - zoneId, - LocalAction - .HackTemporarily(pguid, zone, target, unk, target.HackEffectDuration(user.avatar.hackingSkillLevel())) - ) - case Failure(_) => log.warn(s"Hack message failed on target guid: ${target.GUID}") - } + ask(target.Actor, CommonMessages.Hack(tplayer, target))(timeout = 2 second) + .mapTo[CommonMessages.EntityHackState] + .onComplete { + case Success(_) => + val zone = target.Zone + val zoneId = zone.id + val pguid = tplayer.GUID + log.info(s"${user.Name} hacked a ${target.Definition.Name}") + zone.LocalEvents ! LocalServiceMessage( + zoneId, + LocalAction.TriggerSound(pguid, target.HackSound, tplayer.Position, 30, 0.49803925f) + ) + zone.LocalEvents ! LocalServiceMessage( + zoneId, + LocalAction + .HackTemporarily(pguid, zone, target, unk, target.HackEffectDuration(user.avatar.hackingSkillLevel())) + ) + case Failure(_) => + log.warn(s"Hack message failed on target: ${target.Definition.Name}@${target.GUID.guid}") + } } } diff --git a/src/main/scala/net/psforever/objects/serverobject/hackable/HackableBehavior.scala b/src/main/scala/net/psforever/objects/serverobject/hackable/HackableBehavior.scala index 39fd599cc..d3d1243e1 100644 --- a/src/main/scala/net/psforever/objects/serverobject/hackable/HackableBehavior.scala +++ b/src/main/scala/net/psforever/objects/serverobject/hackable/HackableBehavior.scala @@ -1,29 +1,44 @@ // Copyright (c) 2017 PSForever package net.psforever.objects.serverobject.hackable -import akka.actor.Actor +import akka.actor.{Actor, ActorRef} +import net.psforever.objects.{PlanetSideGameObject, Player} import net.psforever.objects.serverobject.CommonMessages +import scala.annotation.unused + object HackableBehavior { /** - * The logic governing generic `Hackable` objects that use the `Hack` and `ClearHack` message. - * This is a mix-in trait for combining with existing `Receive` logic. - * @see `Hackable` - */ + * The logic governing generic `Hackable` objects that use the `Hack` and `ClearHack` message. + * This is a mix-in trait for combining with existing `Receive` logic. + * @see `Hackable` + */ trait GenericHackable { this: Actor => - def HackableObject: Hackable - - val hackableBehavior: Receive = { - case CommonMessages.Hack(player, _, _) => - val obj = HackableObject - obj.HackedBy = player - sender() ! true + def HackableObject: PlanetSideGameObject with Hackable + val clearHackBehavior: Receive = { case CommonMessages.ClearHack() => - val obj = HackableObject - obj.HackedBy = None + performClearHack(None, sender()) + } + + val hackableBehavior: Receive = clearHackBehavior + .orElse { + case CommonMessages.Hack(player, _, _) => + performHack(player, None, sender()) + } + + def performHack(player: Player, @unused data: Option[Any], replyTo: ActorRef): Unit = { + val obj = HackableObject + obj.HackedBy = player + replyTo ! CommonMessages.EntityHackState(obj, hackState = true) + } + + def performClearHack(@unused data: Option[Any], replyTo: ActorRef): Unit = { + val obj = HackableObject + obj.HackedBy = None + replyTo ! CommonMessages.EntityHackState(obj, hackState = false) } } } diff --git a/src/main/scala/net/psforever/objects/serverobject/resourcesilo/ResourceSiloControl.scala b/src/main/scala/net/psforever/objects/serverobject/resourcesilo/ResourceSiloControl.scala index 0f8eceb58..a892e21b0 100644 --- a/src/main/scala/net/psforever/objects/serverobject/resourcesilo/ResourceSiloControl.scala +++ b/src/main/scala/net/psforever/objects/serverobject/resourcesilo/ResourceSiloControl.scala @@ -1,7 +1,7 @@ // Copyright (c) 2019 PSForever package net.psforever.objects.serverobject.resourcesilo -import akka.actor.{Actor, ActorRef} +import akka.actor.{Actor, ActorRef, Cancellable} import net.psforever.objects.serverobject.CommonMessages import net.psforever.actors.zone.BuildingActor import net.psforever.objects.serverobject.affinity.{FactionAffinity, FactionAffinityBehavior} @@ -53,37 +53,31 @@ class ResourceSiloControl(resourceSilo: ResourceSilo) checkBehavior .orElse(storageBehavior) .orElse { - case CommonMessages.Use(player, _) => + case CommonMessages.Use(_, Some(vehicle: Vehicle)) + if GlobalDefinitions.isBattleFrameVehicle(vehicle.Definition) => val siloFaction = resourceSilo.Faction - val playerFaction = player.Faction - resourceSilo.Zone.Vehicles.find(v => v.PassengerInSeat(player).contains(0)) match { - case Some(vehicle) => - (if (GlobalDefinitions.isBattleFrameVehicle(vehicle.Definition)) { - //bfr's discharge into friendly silos and charge from enemy and neutral silos - if (siloFaction == playerFaction) { - Some(TransferBehavior.Discharging(Ntu.Nanites)) - } else if (resourceSilo.MaxNtuCapacitor * 0.4f < resourceSilo.NtuCapacitor) { - //the bfr never drains below 40% - Some(TransferBehavior.Charging(Ntu.Nanites)) - } else { - None - } - } else if(siloFaction == PlanetSideEmpire.NEUTRAL || siloFaction == playerFaction) { - //ants discharge into neutral and friendly silos - Some(TransferBehavior.Discharging(Ntu.Nanites)) - } else { - None - }) match { - case Some(msg) => - context.system.scheduler.scheduleOnce( - delay = 1000 milliseconds, - vehicle.Actor, - msg - ) - case None => ; - } - case _ => ; + val vehicleFaction = vehicle.Faction + val msgOpt = if (siloFaction == vehicleFaction) { + Some(TransferBehavior.Discharging(Ntu.Nanites)) + } else if (resourceSilo.MaxNtuCapacitor * 0.4f < resourceSilo.NtuCapacitor) { + //the bfr never drains below 40% + Some(TransferBehavior.Charging(Ntu.Nanites)) + } else { + None } + msgOpt.collect { SendNtuMessage(vehicle.Actor, _) } + + case CommonMessages.Use(_, Some(vehicle: Vehicle)) + if vehicle.Definition == GlobalDefinitions.ant => + val siloFaction = resourceSilo.Faction + val vehicleFaction = vehicle.Faction + val msgOpt = if (siloFaction == PlanetSideEmpire.NEUTRAL || siloFaction == vehicleFaction) { + //ants discharge into neutral and friendly silos + Some(TransferBehavior.Discharging(Ntu.Nanites)) + } else { + None + } + msgOpt.collect { SendNtuMessage(vehicle.Actor, _) } case ResourceSilo.LowNtuWarning(enabled: Boolean) => LowNtuWarning(enabled) @@ -91,9 +85,13 @@ class ResourceSiloControl(resourceSilo: ResourceSilo) case ResourceSilo.UpdateChargeLevel(amount: Float) => UpdateChargeLevel(amount) - case _ => ; + case _ => () } + def SendNtuMessage(replyTo: ActorRef, msg: Any): Cancellable = { + context.system.scheduler.scheduleOnce(delay = 1000 milliseconds, replyTo, msg) + } + def LowNtuWarning(enabled: Boolean): Unit = { resourceSilo.LowNtuWarningOn = enabled log.trace(s"LowNtuWarning: Silo ${resourceSilo.GUID} low ntu warning set to $enabled") @@ -147,6 +145,7 @@ class ResourceSiloControl(resourceSilo: ResourceSilo) * The silo will agree to offers until its nanite capacitor is completely full. */ def HandleNtuOffer(sender: ActorRef, src: NtuContainer): Unit = { + //todo if I want to test conditions towards continuing ntu transfers, do those here sender ! (if (resourceSilo.NtuCapacitor < resourceSilo.MaxNtuCapacitor) { Ntu.Request(0, resourceSilo.MaxNtuCapacitor - resourceSilo.NtuCapacitor) } else { diff --git a/src/main/scala/net/psforever/objects/serverobject/terminals/ProximityTerminalControl.scala b/src/main/scala/net/psforever/objects/serverobject/terminals/ProximityTerminalControl.scala index 02191e393..adea8ef27 100644 --- a/src/main/scala/net/psforever/objects/serverobject/terminals/ProximityTerminalControl.scala +++ b/src/main/scala/net/psforever/objects/serverobject/terminals/ProximityTerminalControl.scala @@ -5,6 +5,7 @@ import akka.actor.{ActorRef, Cancellable} import net.psforever.objects.sourcing.AmenitySource import org.log4s.Logger +import scala.annotation.unused import scala.collection.mutable import scala.concurrent.duration._ // @@ -121,6 +122,7 @@ class ProximityTerminalControl(term: Terminal with ProximityUnit) } def unpoweredStateLogic : Receive = commonBehavior + .orElse(clearHackBehavior) .orElse { case CommonMessages.Use(_, _) => log.warn(s"unexpected format for CommonMessages.Use in this context") @@ -145,7 +147,7 @@ class ProximityTerminalControl(term: Terminal with ProximityUnit) isPowered && super.tryAutoRepair() } - def Use(target: PlanetSideGameObject, zone: String, callback: ActorRef): Unit = { + def Use(target: PlanetSideGameObject, @unused zone: String, callback: ActorRef): Unit = { val hadNoUsers = term.NumberUsers == 0 if (term.AddUser(target)) { log.trace(s"ProximityTerminal.Use: unit ${term.Definition.Name}@${term.GUID.guid} will act on $target") @@ -169,7 +171,7 @@ class ProximityTerminalControl(term: Terminal with ProximityUnit) } } - def Unuse(target: PlanetSideGameObject, zone: String): Unit = { + def Unuse(target: PlanetSideGameObject, @unused zone: String): Unit = { val whereTarget = term.Targets.indexWhere(_ eq target) val previousUsers = term.NumberUsers val hadUsers = previousUsers > 0 @@ -223,7 +225,7 @@ object ProximityTerminalControl { * @param target the object being affected by the unit */ def selectAndTryProximityUnitBehavior( - callback: ActorRef, + @unused callback: ActorRef, terminal: Terminal with ProximityUnit, target: PlanetSideGameObject ): Boolean = { diff --git a/src/main/scala/net/psforever/objects/serverobject/terminals/TerminalControl.scala b/src/main/scala/net/psforever/objects/serverobject/terminals/TerminalControl.scala index a020e8426..46f596aa8 100644 --- a/src/main/scala/net/psforever/objects/serverobject/terminals/TerminalControl.scala +++ b/src/main/scala/net/psforever/objects/serverobject/terminals/TerminalControl.scala @@ -19,24 +19,24 @@ import net.psforever.services.local.{LocalAction, LocalServiceMessage} * @param term the `Terminal` object being governed */ class TerminalControl(term: Terminal) - extends PoweredAmenityControl + extends PoweredAmenityControl with FactionAffinityBehavior.Check with HackableBehavior.GenericHackable with DamageableAmenity with RepairableAmenity with AmenityAutoRepair { - def FactionObject = term - def HackableObject = term - def DamageableObject = term - def RepairableObject = term - def AutoRepairObject = term + def FactionObject: Terminal = term + def HackableObject: Terminal = term + def DamageableObject: Terminal = term + def RepairableObject: Terminal = term + def AutoRepairObject: Terminal = term val commonBehavior: Receive = checkBehavior .orElse(takesDamage) .orElse(canBeRepairedByNanoDispenser) .orElse(autoRepairBehavior) - def poweredStateLogic : Receive = + def poweredStateLogic: Receive = commonBehavior .orElse(hackableBehavior) .orElse { @@ -53,18 +53,19 @@ class TerminalControl(term: Terminal) GenericHackables.FinishHacking(term, player, 3212836864L), GenericHackables.HackingTickAction(progressType = 1, player, term, item.GUID) ) - case _ => ; + case _ => () } - case _ => ; + case _ => () } def unpoweredStateLogic : Receive = commonBehavior + .orElse(clearHackBehavior) .orElse { case Terminal.Request(player, msg) => sender() ! Terminal.TerminalMessage(player, msg, Terminal.NoDeal()) - case _ => ; + case _ => () } override protected def DamageAwareness(target: Target, cause: DamageResult, amount: Any) : Unit = { diff --git a/src/main/scala/net/psforever/objects/serverobject/terminals/capture/CaptureTerminals.scala b/src/main/scala/net/psforever/objects/serverobject/terminals/capture/CaptureTerminals.scala index 0ed0f7161..7144d4803 100644 --- a/src/main/scala/net/psforever/objects/serverobject/terminals/capture/CaptureTerminals.scala +++ b/src/main/scala/net/psforever/objects/serverobject/terminals/capture/CaptureTerminals.scala @@ -1,6 +1,5 @@ package net.psforever.objects.serverobject.terminals.capture -import akka.util.Timeout import net.psforever.objects.Player import net.psforever.objects.serverobject.CommonMessages import net.psforever.objects.sourcing.PlayerSource @@ -10,17 +9,16 @@ import scala.util.{Failure, Success} object CaptureTerminals {import scala.concurrent.duration._ private val log = org.log4s.getLogger("CaptureTerminals") - private implicit val timeout: Timeout = 1.second /** - * The process of hacking an object is completed. - * Pass the message onto the hackable object and onto the local events system. - * @param target the `Hackable` object that has been hacked - * @param hackingPlayer The player that hacked the control console - * @param unk na; - * used by `HackMessage` as `unk5` - * @see `HackMessage` - */ + * The process of hacking an object is completed. + * Pass the message onto the hackable object and onto the local events system. + * @param target the `Hackable` object that has been hacked + * @param hackingPlayer The player that hacked the control console + * @param unk na; + * used by `HackMessage` as `unk5` + * @see `HackMessage` + */ //TODO add params here depending on which params in HackMessage are important def FinishHackingCaptureConsole(target: CaptureTerminal, hackingPlayer: Player, unk: Long)(): Unit = { import akka.pattern.ask @@ -28,31 +26,33 @@ object CaptureTerminals {import scala.concurrent.duration._ log.info(s"${hackingPlayer.toString} hacked a ${target.Definition.Name}") // Wait for the target actor to set the HackedBy property import scala.concurrent.ExecutionContext.Implicits.global - ask(target.Actor, CommonMessages.Hack(hackingPlayer, target)).mapTo[Boolean].onComplete { - case Success(_) => - val zone = target.Zone - val zoneid = zone.id - val events = zone.LocalEvents - val isResecured = hackingPlayer.Faction == target.Faction - events ! LocalServiceMessage( - zoneid, - LocalAction.TriggerSound(hackingPlayer.GUID, target.HackSound, hackingPlayer.Position, 30, 0.49803925f) - ) - if (isResecured) { - // Resecure the CC + ask(target.Actor, CommonMessages.Hack(hackingPlayer, target))(timeout = 2 second) + .mapTo[CommonMessages.EntityHackState] + .onComplete { + case Success(_) => + val zone = target.Zone + val zoneid = zone.id + val events = zone.LocalEvents + val isResecured = hackingPlayer.Faction == target.Faction events ! LocalServiceMessage( zoneid, - LocalAction.ResecureCaptureTerminal(target, PlayerSource(hackingPlayer)) + LocalAction.TriggerSound(hackingPlayer.GUID, target.HackSound, hackingPlayer.Position, 30, 0.49803925f) ) - } else { - // Start the CC hack timer - events ! LocalServiceMessage( - zoneid, - LocalAction.StartCaptureTerminalHack(target) - ) - } - case Failure(_) => - log.warn(s"Hack message failed on target guid: ${target.GUID}") - } + if (isResecured) { + // Resecure the CC + events ! LocalServiceMessage( + zoneid, + LocalAction.ResecureCaptureTerminal(target, PlayerSource(hackingPlayer)) + ) + } else { + // Start the CC hack timer + events ! LocalServiceMessage( + zoneid, + LocalAction.StartCaptureTerminalHack(target) + ) + } + case Failure(_) => + log.warn(s"Hack message failed on target guid: ${target.GUID}") + } } } diff --git a/src/main/scala/net/psforever/objects/serverobject/terminals/implant/ImplantTerminalMechControl.scala b/src/main/scala/net/psforever/objects/serverobject/terminals/implant/ImplantTerminalMechControl.scala index 839c82d8a..f4f53b662 100644 --- a/src/main/scala/net/psforever/objects/serverobject/terminals/implant/ImplantTerminalMechControl.scala +++ b/src/main/scala/net/psforever/objects/serverobject/terminals/implant/ImplantTerminalMechControl.scala @@ -125,7 +125,7 @@ class ImplantTerminalMechControl(mech: ImplantTerminalMech) seat.unmount(player) player.VehicleSeated = None if (player.HasGUID) { - events ! VehicleServiceMessage(zoneId, VehicleAction.KickPassenger(player.GUID, 4, false, guid)) + events ! VehicleServiceMessage(zoneId, VehicleAction.KickPassenger(player.GUID, 4, unk2 = false, guid)) } case None => ; } diff --git a/src/main/scala/net/psforever/objects/serverobject/turret/FacilityTurretControl.scala b/src/main/scala/net/psforever/objects/serverobject/turret/FacilityTurretControl.scala index e0dec604c..09e0973f1 100644 --- a/src/main/scala/net/psforever/objects/serverobject/turret/FacilityTurretControl.scala +++ b/src/main/scala/net/psforever/objects/serverobject/turret/FacilityTurretControl.scala @@ -104,9 +104,14 @@ class FacilityTurretControl(turret: FacilityTurret) } override protected def tryMount(obj: PlanetSideServerObject with Mountable, seatNumber: Int, player: Player): Boolean = { + val originalAutoState = AutomaticOperation AutomaticOperation = false //turn off + if (AutomaticOperationPossible && JammableObject.Jammed) { + val zone = TurretObject.Zone + AutomatedTurretBehavior.stopTracking(zone, zone.id, TurretObject.GUID) //can not recover lost jamming aggro + } if (!super.tryMount(obj, seatNumber, player)) { - AutomaticOperation = true //revert? + AutomaticOperation = originalAutoState //revert false } else { true @@ -215,6 +220,15 @@ class FacilityTurretControl(turret: FacilityTurret) !TurretObject.isUpgrading } + override def AutomaticOperationPossible: Boolean = { + super.AutomaticOperationPossible && + (turret.Owner match { + case b: Building if b.CaptureTerminal.isEmpty => false + case b: Building => !b.CaptureTerminal.exists(_.Definition == GlobalDefinitions.secondary_capture) + case _ => false + }) + } + private def primaryWeaponFireModeOnly(): Unit = { if (testToResetToDefaultFireMode) { val zone = TurretObject.Zone @@ -289,16 +303,15 @@ class FacilityTurretControl(turret: FacilityTurret) } override def TryJammerEffectActivate(target: Any, cause: DamageResult): Unit = { - val startsUnjammed = !JammableObject.Jammed super.TryJammerEffectActivate(target, cause) - if (JammableObject.Jammed && AutomatedTurretObject.Definition.AutoFire.exists(_.retaliatoryDelay > 0)) { - if (startsUnjammed) { - AutomaticOperation = false - } - //look in direction of cause of jamming - val zone = JammableObject.Zone - AutomatedTurretBehavior.getAttackVectorFromCause(zone, cause).foreach { attacker => - AutomatedTurretBehavior.startTracking(zone, zone.id, JammableObject.GUID, List(attacker.GUID)) + if (AutomaticOperationPossible && AutomaticOperation && JammableObject.Jammed) { + AutomaticOperation = false + if (!MountableObject.Seats.values.exists(_.isOccupied) && AutomatedTurretObject.Definition.AutoFire.exists(_.retaliatoryDelay > 0)) { + //look in direction of cause of jamming + val zone = JammableObject.Zone + AutomatedTurretBehavior.getAttackVectorFromCause(zone, cause).foreach { attacker => + AutomatedTurretBehavior.startTracking(zone, zone.id, JammableObject.GUID, List(attacker.GUID)) + } } } } diff --git a/src/main/scala/net/psforever/objects/serverobject/turret/auto/AutomatedTurretBehavior.scala b/src/main/scala/net/psforever/objects/serverobject/turret/auto/AutomatedTurretBehavior.scala index 1c2f64354..fad4c5c34 100644 --- a/src/main/scala/net/psforever/objects/serverobject/turret/auto/AutomatedTurretBehavior.scala +++ b/src/main/scala/net/psforever/objects/serverobject/turret/auto/AutomatedTurretBehavior.scala @@ -80,6 +80,8 @@ trait AutomatedTurretBehavior { Actor.emptyBehavior } + def AutomaticOperationPossible: Boolean = autoStats.isDefined + def AutomaticOperation: Boolean = automaticOperation /** @@ -111,7 +113,7 @@ trait AutomatedTurretBehavior { * @return `true`, if it would be possible for automated behavior to become operational; * `false`, otherwise */ - protected def AutomaticOperationFunctionalityChecks: Boolean = { autoStats.isDefined } + protected def AutomaticOperationFunctionalityChecks: Boolean = AutomaticOperationPossible /** * The last time weapons fire from the turret was confirmed by this control agency. diff --git a/src/main/scala/net/psforever/objects/zones/Zone.scala b/src/main/scala/net/psforever/objects/zones/Zone.scala index c8ef2e808..07c9ed581 100644 --- a/src/main/scala/net/psforever/objects/zones/Zone.scala +++ b/src/main/scala/net/psforever/objects/zones/Zone.scala @@ -288,7 +288,7 @@ class Zone(val id: String, val map: ZoneMap, zoneNumber: Int) { * @return synchronized reference to the globally unique identifier system */ def GUID(hub: NumberPoolHub): Boolean = { - if (actor == null && guid.Pools.values.foldLeft(0)(_ + _.Count) == 0) { + if (actor == Default.typed.Actor && guid.Pools.values.foldLeft(0)(_ + _.Count) == 0) { import org.fusesource.jansi.Ansi.Color.RED import org.fusesource.jansi.Ansi.ansi println( diff --git a/src/main/scala/net/psforever/packet/game/DeployableObjectsInfoMessage.scala b/src/main/scala/net/psforever/packet/game/DeployableObjectsInfoMessage.scala index 4c03ec112..916c8b0a3 100644 --- a/src/main/scala/net/psforever/packet/game/DeployableObjectsInfoMessage.scala +++ b/src/main/scala/net/psforever/packet/game/DeployableObjectsInfoMessage.scala @@ -26,7 +26,7 @@ object DeploymentAction extends Enumeration { object DeployableIcon extends Enumeration { type Type = Value - val Boomer, HEMine, MotionAlarmSensor, SpitfireTurret, RouterTelepad, DisruptorMine, ShadowTurret, CerebusTurret, + val Boomer, HEMine, MotionAlarmSensor, SpitfireTurret, RouterTelepad, DisruptorMine, ShadowTurret, cerberusTurret, TRAP, AegisShieldGenerator, FieldTurret, SensorDisruptor = Value implicit val codec: Codec[DeployableIcon.Value] = PacketHelpers.createEnumerationCodec(this, uint4L) diff --git a/src/main/scala/net/psforever/packet/game/PlanetsideAttributeMessage.scala b/src/main/scala/net/psforever/packet/game/PlanetsideAttributeMessage.scala index d62590404..a6fb90707 100644 --- a/src/main/scala/net/psforever/packet/game/PlanetsideAttributeMessage.scala +++ b/src/main/scala/net/psforever/packet/game/PlanetsideAttributeMessage.scala @@ -32,7 +32,7 @@ import scodec.codecs._ * `86 - max spitfire turrets`
* `87 - max motion sensors`
* `88 - max shadow turrets`
- * `89 - max cerebus turrets`
+ * `89 - max cerberus turrets`
* `90 - max Aegis shield generators`
* `91 - max TRAPs`
* `92 - max OMFTs`
@@ -43,7 +43,7 @@ import scodec.codecs._ * `97 - spitfire turrets`
* `98 - motion sensors`
* `99 - shadow turrets`
- * `100 - cerebus turrets`
+ * `100 - cerberus turrets`
* `101 - Aegis shield generators`
* `102 - TRAPSs`
* `103 - OMFTs`
diff --git a/src/test/scala/base/ActorTest.scala b/src/test/scala/base/ActorTest.scala index 2cbbaca0b..6249c31ff 100644 --- a/src/test/scala/base/ActorTest.scala +++ b/src/test/scala/base/ActorTest.scala @@ -4,6 +4,7 @@ package base import akka.actor.{Actor, ActorRef, ActorSystem, Props} import akka.testkit.{ImplicitSender, TestKit} import com.typesafe.config.ConfigFactory +import net.psforever.objects.Default import org.scalatest.BeforeAndAfterAll import org.scalatest.matchers.should.Matchers import org.scalatest.wordspec.AnyWordSpecLike @@ -19,6 +20,7 @@ abstract class ActorTest(sys: ActorSystem = ActorSystem("system", ConfigFactory. with AnyWordSpecLike with Matchers with BeforeAndAfterAll { + Default(sys) override def afterAll(): Unit = { TestKit.shutdownActorSystem(system) } diff --git a/src/test/scala/objects/DamageableTest.scala b/src/test/scala/objects/DamageableTest.scala index 457dd4bb4..bd2381fb9 100644 --- a/src/test/scala/objects/DamageableTest.scala +++ b/src/test/scala/objects/DamageableTest.scala @@ -775,7 +775,7 @@ class DamageableWeaponTurretDamageTest extends ActorTest { zone.AvatarEvents = avatarProbe.ref zone.VehicleEvents = vehicleProbe.ref val turret = new TurretDeployable(GlobalDefinitions.portable_manned_turret_tr) //2 - turret.Actor = system.actorOf(Props(classOf[TurretDeployableControl], turret), "turret-control") + turret.Actor = system.actorOf(Props(classOf[FieldTurretControl], turret), "turret-control") turret.Zone = zone turret.Position = Vector3(1, 0, 0) turret.LogActivity(SpawningActivity(SourceEntry(turret), zone.Number, None)) //seed a spawn event @@ -873,7 +873,7 @@ class DamageableWeaponTurretJammerTest extends ActorTest { zone.VehicleEvents = vehicleProbe.ref val turret = new TurretDeployable(GlobalDefinitions.portable_manned_turret_tr) //2, 5, 6 - turret.Actor = system.actorOf(Props(classOf[TurretDeployableControl], turret), "turret-control") + turret.Actor = system.actorOf(Props(classOf[FieldTurretControl], turret), "turret-control") turret.Zone = zone turret.Position = Vector3(1, 0, 0) val turretWeapon: Tool = turret.Weapons.values.head.Equipment.get.asInstanceOf[Tool] diff --git a/src/test/scala/objects/DefaultTest.scala b/src/test/scala/objects/DefaultTest.scala index 329efc410..cbf5ca7a5 100644 --- a/src/test/scala/objects/DefaultTest.scala +++ b/src/test/scala/objects/DefaultTest.scala @@ -25,18 +25,17 @@ class DefaultActorStartedTest extends ActorTest { "Default.Actor" should { "send messages to deadLetters" in { //after being started - Default(system) val probe = new TestProbe(system) system.eventStream.subscribe(probe.ref, classOf[DeadLetter]) Default.Actor ! "hello world" - val msg1 = probe.receiveOne(250 milliseconds) + val msg1 = probe.receiveOne(5000 milliseconds) assert(msg1.isInstanceOf[DeadLetter]) assert(msg1.asInstanceOf[DeadLetter].message equals "hello world") //if it was stopped system.stop(Default.Actor) Default.Actor ! "hello world" - val msg2 = probe.receiveOne(250 milliseconds) + val msg2 = probe.receiveOne(5000 milliseconds) assert(msg2.isInstanceOf[DeadLetter]) assert(msg2.asInstanceOf[DeadLetter].message equals "hello world") } diff --git a/src/test/scala/objects/DeployableTest.scala b/src/test/scala/objects/DeployableTest.scala index 26dac7579..1708ff74a 100644 --- a/src/test/scala/objects/DeployableTest.scala +++ b/src/test/scala/objects/DeployableTest.scala @@ -138,14 +138,14 @@ class TurretDeployableTest extends Specification { DeployedItem.portable_manned_turret_nc.id, DeployedItem.portable_manned_turret_vs.id ).foreach(id => { - try { new TurretDeployableDefinition(id) } + try { new TurretDeployableDefinition(id) { } } catch { case _: Exception => ko } }) ok } "define (invalid object)" in { - new TurretDeployableDefinition(5) must throwA[NoSuchElementException] //wrong object id altogether + new TurretDeployableDefinition(objectId = 5) { } must throwA[NoSuchElementException] //wrong object id altogether } "construct" in { @@ -212,7 +212,7 @@ class DeployableMake extends Specification { } } - "construct a cerebus turret" in { + "construct a cerberus turret" in { val func = Deployables.Make(DeployedItem.spitfire_aa) func() match { case obj: TurretDeployable if obj.Definition == GlobalDefinitions.spitfire_aa => ok @@ -584,7 +584,7 @@ class TurretControlConstructTest extends ActorTest { "TurretControl" should { "construct" in { val obj = new TurretDeployable(GlobalDefinitions.spitfire_turret) - system.actorOf(Props(classOf[TurretDeployableControl], obj), s"${obj.Definition.Name}_test") + system.actorOf(Props(classOf[SmallTurretControl], obj), s"${obj.Definition.Name}_test") } } } @@ -629,7 +629,7 @@ class TurretControlMountTest extends ActorTest { override def SetupNumberPools() = {} this.actor = new TestProbe(system).ref.toTyped[ZoneActor.Command] } - obj.Actor = system.actorOf(Props(classOf[TurretDeployableControl], obj), s"${obj.Definition.Name}_test") + obj.Actor = system.actorOf(Props(classOf[FieldTurretControl], obj), s"${obj.Definition.Name}_test") assert(obj.Seats(0).occupant.isEmpty) val player1 = Player(Avatar(0, "test1", PlanetSideEmpire.TR, CharacterSex.Male, 0, CharacterVoice.Mute)) @@ -649,7 +649,7 @@ class TurretControlBlockMountTest extends ActorTest { "block mounting by others if already mounted by someone" in { val obj = new TurretDeployable(GlobalDefinitions.portable_manned_turret_tr) { GUID = PlanetSideGUID(1) } obj.Faction = PlanetSideEmpire.TR - obj.Actor = system.actorOf(Props(classOf[TurretDeployableControl], obj), s"${obj.Definition.Name}_test") + obj.Actor = system.actorOf(Props(classOf[FieldTurretControl], obj), s"${obj.Definition.Name}_test") obj.Zone = new Zone("test", new ZoneMap("test"), 0) { override def SetupNumberPools() = {} this.actor = new TestProbe(system).ref.toTyped[ZoneActor.Command] @@ -681,7 +681,7 @@ class TurretControlBlockBetrayalMountTest extends ActorTest { "block mounting by players of another faction" in { val obj = new TurretDeployable(GlobalDefinitions.portable_manned_turret_tr) { GUID = PlanetSideGUID(1) } obj.Faction = PlanetSideEmpire.TR - obj.Actor = system.actorOf(Props(classOf[TurretDeployableControl], obj), s"${obj.Definition.Name}_test") + obj.Actor = system.actorOf(Props(classOf[FieldTurretControl], obj), s"${obj.Definition.Name}_test") assert(obj.Seats(0).occupant.isEmpty) val player = Player(Avatar(0, "test", PlanetSideEmpire.VS, CharacterSex.Male, 0, CharacterVoice.Mute)) @@ -705,7 +705,7 @@ class TurretControlDismountTest extends ActorTest { override def SetupNumberPools() = {} this.actor = new TestProbe(system).ref.toTyped[ZoneActor.Command] } - obj.Actor = system.actorOf(Props(classOf[TurretDeployableControl], obj), s"${obj.Definition.Name}_test") + obj.Actor = system.actorOf(Props(classOf[FieldTurretControl], obj), s"${obj.Definition.Name}_test") assert(obj.Seats(0).occupant.isEmpty) val player = Player(Avatar(0, "test", PlanetSideEmpire.TR, CharacterSex.Male, 0, CharacterVoice.Mute)) @@ -746,7 +746,7 @@ class TurretControlBetrayalMountTest extends ActorTest { } } obj.Faction = PlanetSideEmpire.TR - obj.Actor = system.actorOf(Props(classOf[TurretDeployableControl], obj), s"${obj.Definition.Name}_test") + obj.Actor = system.actorOf(Props(classOf[FieldTurretControl], obj), s"${obj.Definition.Name}_test") val probe = new TestProbe(system) assert(obj.Seats(0).occupant.isEmpty) diff --git a/src/test/scala/objects/DeployableToolboxTest.scala b/src/test/scala/objects/DeployableToolboxTest.scala index e608812ad..a524e909d 100644 --- a/src/test/scala/objects/DeployableToolboxTest.scala +++ b/src/test/scala/objects/DeployableToolboxTest.scala @@ -819,13 +819,13 @@ class DeployableToolboxTest extends Specification { val obj = new DeployableToolbox obj.Initialize(Set(CombatEngineering)) - val cerebus = new TurretDeployable(GlobalDefinitions.spitfire_aa) //cerebus turret - obj.Valid(cerebus) mustEqual false + val cerberus = new TurretDeployable(GlobalDefinitions.spitfire_aa) //cerberus turret + obj.Valid(cerberus) mustEqual false obj.CountDeployable(DeployedItem.spitfire_aa).productIterator.toList mustEqual List(0, 0) obj.UpdateMaxCounts(Set(CombatEngineering, AdvancedEngineering)) - obj.Valid(cerebus) mustEqual true + obj.Valid(cerberus) mustEqual true obj.CountDeployable(DeployedItem.spitfire_aa).productIterator.toList mustEqual List(0, 5) } diff --git a/src/test/scala/objects/PlayerControlTest.scala b/src/test/scala/objects/PlayerControlTest.scala index d10e7a85b..aad7d21ca 100644 --- a/src/test/scala/objects/PlayerControlTest.scala +++ b/src/test/scala/objects/PlayerControlTest.scala @@ -774,124 +774,124 @@ class PlayerControlDeathStandingTest extends ActorTest { // } //} -class PlayerControlInteractWithWaterTest extends ActorTest { - val player1: Player = - Player(Avatar(0, "TestCharacter1", PlanetSideEmpire.TR, CharacterSex.Male, 0, CharacterVoice.Mute)) //guid=1 - val avatarProbe: TestProbe = TestProbe() - val guid = new NumberPoolHub(new MaxNumberSource(15)) - val pool: Pool = Pool(EnvironmentAttribute.Water, DeepSquare(-1, 10, 10, 0, 0)) - val zone: Zone = new Zone( - id = "test", - new ZoneMap(name = "test-map") { - environment = List(pool) - }, - zoneNumber = 0 - ) { - override def SetupNumberPools(): Unit = {} - GUID(guid) - override def LivePlayers: List[Player] = List(player1) - override def AvatarEvents: ClassicActorRef = avatarProbe.ref - } - zone.blockMap.addTo(player1) - zone.blockMap.addTo(pool) +//class PlayerControlInteractWithWaterTest extends ActorTest { +// val player1: Player = +// Player(Avatar(0, "TestCharacter1", PlanetSideEmpire.TR, CharacterSex.Male, 0, CharacterVoice.Mute)) //guid=1 +// val avatarProbe: TestProbe = TestProbe() +// val guid = new NumberPoolHub(new MaxNumberSource(15)) +// val pool: Pool = Pool(EnvironmentAttribute.Water, DeepSquare(-1, 10, 10, 0, 0)) +// val zone: Zone = new Zone( +// id = "test", +// new ZoneMap(name = "test-map") { +// environment = List(pool) +// }, +// zoneNumber = 0 +// ) { +// override def SetupNumberPools(): Unit = {} +// GUID(guid) +// override def LivePlayers: List[Player] = List(player1) +// override def AvatarEvents: ClassicActorRef = avatarProbe.ref +// } +// zone.blockMap.addTo(player1) +// zone.blockMap.addTo(pool) +// +// player1.Zone = zone +// player1.Spawn() +// guid.register(player1.avatar.locker, 5) +// val (probe, avatarActor) = PlayerControlTest.DummyAvatar(system) +// player1.Actor = system.actorOf(Props(classOf[PlayerControl], player1, avatarActor), "player1-control") +// +// guid.register(player1, 1) +// +// "PlayerControl" should { +// "cause drowning when player steps too deep in water" in { +// assert(player1.Health == 100) +// player1.Position = Vector3(5,5,-3) //right in the pool +// player1.zoneInteractions() //trigger +// +// val msg_drown = avatarProbe.receiveOne(250 milliseconds) +// assert( +// msg_drown match { +// case AvatarServiceMessage( +// "TestCharacter1", +// AvatarAction.OxygenState(OxygenStateTarget(PlanetSideGUID(1), _, OxygenState.Suffocation, 100f), _) +// ) => true +// case _ => false +// } +// ) +// //player will die in 60s +// //detailing these death messages is not necessary +// assert(player1.Health == 100) +// probe.receiveOne(65 seconds) //wait until our implants deinitialize +// assert(player1.Health == 0) //ded +// } +// } +//} - player1.Zone = zone - player1.Spawn() - guid.register(player1.avatar.locker, 5) - val (probe, avatarActor) = PlayerControlTest.DummyAvatar(system) - player1.Actor = system.actorOf(Props(classOf[PlayerControl], player1, avatarActor), "player1-control") - - guid.register(player1, 1) - - "PlayerControl" should { - "cause drowning when player steps too deep in water" in { - assert(player1.Health == 100) - player1.Position = Vector3(5,5,-3) //right in the pool - player1.zoneInteractions() //trigger - - val msg_drown = avatarProbe.receiveOne(250 milliseconds) - assert( - msg_drown match { - case AvatarServiceMessage( - "TestCharacter1", - AvatarAction.OxygenState(OxygenStateTarget(PlanetSideGUID(1), _, OxygenState.Suffocation, 100f), _) - ) => true - case _ => false - } - ) - //player will die in 60s - //detailing these death messages is not necessary - assert(player1.Health == 100) - probe.receiveOne(65 seconds) //wait until our implants deinitialize - assert(player1.Health == 0) //ded - } - } -} - -class PlayerControlStopInteractWithWaterTest extends ActorTest { - val player1: Player = - Player(Avatar(0, "TestCharacter1", PlanetSideEmpire.TR, CharacterSex.Male, 0, CharacterVoice.Mute)) //guid=1 - val avatarProbe: TestProbe = TestProbe() - val guid = new NumberPoolHub(new MaxNumberSource(15)) - val pool: Pool = Pool(EnvironmentAttribute.Water, DeepSquare(-1, 10, 10, 0, 0)) - val zone: Zone = new Zone( - id = "test", - new ZoneMap(name = "test-map") { - environment = List(pool) - }, - zoneNumber = 0 - ) { - override def SetupNumberPools(): Unit = {} - GUID(guid) - override def LivePlayers: List[Player] = List(player1) - override def AvatarEvents: ClassicActorRef = avatarProbe.ref - } - zone.blockMap.addTo(player1) - zone.blockMap.addTo(pool) - - player1.Zone = zone - player1.Spawn() - guid.register(player1.avatar.locker, 5) - val (probe, avatarActor) = PlayerControlTest.DummyAvatar(system) - player1.Actor = system.actorOf(Props(classOf[PlayerControl], player1, avatarActor), "player1-control") - - guid.register(player1, 1) - - "PlayerControl" should { - "stop drowning if player steps out of deep water" in { - assert(player1.Health == 100) - player1.Position = Vector3(5,5,-3) //right in the pool - player1.zoneInteractions() //trigger - - val msg_drown = avatarProbe.receiveOne(250 milliseconds) - assert( - msg_drown match { - case AvatarServiceMessage( - "TestCharacter1", - AvatarAction.OxygenState(OxygenStateTarget(PlanetSideGUID(1), _, OxygenState.Suffocation, 100f), _) - ) => true - case _ => false - } - ) - //player would normally die in 60s - player1.Position = Vector3.Zero //pool's closed - player1.zoneInteractions() //trigger - val msg_recover = avatarProbe.receiveOne(250 milliseconds) - assert( - msg_recover match { - case AvatarServiceMessage( - "TestCharacter1", - AvatarAction.OxygenState(OxygenStateTarget(PlanetSideGUID(1), _, OxygenState.Recovery, _), _) - ) => true - case _ => false - } - ) - assert(player1.Health == 100) //still alive? - probe.expectNoMessage(65 seconds) - assert(player1.Health == 100) //yep, still alive - } - } -} +//class PlayerControlStopInteractWithWaterTest extends ActorTest { +// val player1: Player = +// Player(Avatar(0, "TestCharacter1", PlanetSideEmpire.TR, CharacterSex.Male, 0, CharacterVoice.Mute)) //guid=1 +// val avatarProbe: TestProbe = TestProbe() +// val guid = new NumberPoolHub(new MaxNumberSource(15)) +// val pool: Pool = Pool(EnvironmentAttribute.Water, DeepSquare(-1, 10, 10, 0, 0)) +// val zone: Zone = new Zone( +// id = "test", +// new ZoneMap(name = "test-map") { +// environment = List(pool) +// }, +// zoneNumber = 0 +// ) { +// override def SetupNumberPools(): Unit = {} +// GUID(guid) +// override def LivePlayers: List[Player] = List(player1) +// override def AvatarEvents: ClassicActorRef = avatarProbe.ref +// } +// zone.blockMap.addTo(player1) +// zone.blockMap.addTo(pool) +// +// player1.Zone = zone +// player1.Spawn() +// guid.register(player1.avatar.locker, 5) +// val (probe, avatarActor) = PlayerControlTest.DummyAvatar(system) +// player1.Actor = system.actorOf(Props(classOf[PlayerControl], player1, avatarActor), "player1-control") +// +// guid.register(player1, 1) +// +// "PlayerControl" should { +// "stop drowning if player steps out of deep water" in { +// assert(player1.Health == 100) +// player1.Position = Vector3(5,5,-3) //right in the pool +// player1.zoneInteractions() //trigger +// +// val msg_drown = avatarProbe.receiveOne(250 milliseconds) +// assert( +// msg_drown match { +// case AvatarServiceMessage( +// "TestCharacter1", +// AvatarAction.OxygenState(OxygenStateTarget(PlanetSideGUID(1), _, OxygenState.Suffocation, 100f), _) +// ) => true +// case _ => false +// } +// ) +// //player would normally die in 60s +// player1.Position = Vector3.Zero //pool's closed +// player1.zoneInteractions() //trigger +// val msg_recover = avatarProbe.receiveOne(250 milliseconds) +// assert( +// msg_recover match { +// case AvatarServiceMessage( +// "TestCharacter1", +// AvatarAction.OxygenState(OxygenStateTarget(PlanetSideGUID(1), _, OxygenState.Recovery, _), _) +// ) => true +// case _ => false +// } +// ) +// assert(player1.Health == 100) //still alive? +// probe.expectNoMessage(65 seconds) +// assert(player1.Health == 100) //yep, still alive +// } +// } +//} class PlayerControlInteractWithLavaTest extends ActorTest { val player1: Player = diff --git a/src/test/scala/objects/ZoneTest.scala b/src/test/scala/objects/ZoneTest.scala index 6c4f4dfe6..74cd60ddc 100644 --- a/src/test/scala/objects/ZoneTest.scala +++ b/src/test/scala/objects/ZoneTest.scala @@ -486,9 +486,13 @@ class ZonePopulationTest extends ActorTest { class ZoneGroundDropItemTest extends ActorTest { val item = AmmoBox(GlobalDefinitions.bullet_9mm) val hub = new NumberPoolHub(new MaxNumberSource(20)) + val zone = new Zone( + "test", + new ZoneMap("test-map"), 0) { + override def SetupNumberPools() = {} + GUID(hub) + } hub.register(item, 10) - val zone = new Zone("test", new ZoneMap("test-map"), 0) { override def SetupNumberPools() = {} } - zone.GUID(hub) zone.actor = system.spawn(ZoneActor(zone), ZoneTest.TestName) expectNoMessage(200 milliseconds) @@ -511,9 +515,13 @@ class ZoneGroundDropItemTest extends ActorTest { class ZoneGroundCanNotDropItem1Test extends ActorTest { val item = AmmoBox(GlobalDefinitions.bullet_9mm) val hub = new NumberPoolHub(new MaxNumberSource(20)) + val zone = new Zone( + "test", + new ZoneMap("test-map"), 0) { + override def SetupNumberPools() = {} + GUID(hub) + } //hub.register(item, 10) //!important - val zone = new Zone("test", new ZoneMap("test-map"), 0) { override def SetupNumberPools() = {} } - zone.GUID(hub) zone.actor = system.spawn(ZoneActor(zone), ZoneTest.TestName) expectNoMessage(200 milliseconds) @@ -536,9 +544,13 @@ class ZoneGroundCanNotDropItem1Test extends ActorTest { class ZoneGroundCanNotDropItem2Test extends ActorTest { val item = AmmoBox(GlobalDefinitions.bullet_9mm) val hub = new NumberPoolHub(new MaxNumberSource(20)) - hub.register(item, 10) //!important - val zone = new Zone("test", new ZoneMap("test-map"), 0) { override def SetupNumberPools() = {} } - //zone.GUID(hub) //!important + val zone = new Zone( + "test", + new ZoneMap("test-map"), 0) { + override def SetupNumberPools() = {} + //GUID(hub) !important + } + hub.register(item, 10) zone.actor = system.spawn(ZoneActor(zone), ZoneTest.TestName) expectNoMessage(200 milliseconds) @@ -561,9 +573,13 @@ class ZoneGroundCanNotDropItem2Test extends ActorTest { class ZoneGroundCanNotDropItem3Test extends ActorTest { val item = AmmoBox(GlobalDefinitions.bullet_9mm) val hub = new NumberPoolHub(new MaxNumberSource(20)) - hub.register(item, 10) //!important - val zone = new Zone("test", new ZoneMap("test-map"), 0) { override def SetupNumberPools() = {} } - zone.GUID(hub) //!important + val zone = new Zone( + "test", + new ZoneMap("test-map"), 0) { + override def SetupNumberPools() = {} + GUID(hub) + } + hub.register(item, 10) zone.actor = system.spawn(ZoneActor(zone), ZoneTest.TestName) expectNoMessage(200 milliseconds) @@ -594,9 +610,13 @@ class ZoneGroundCanNotDropItem3Test extends ActorTest { class ZoneGroundPickupItemTest extends ActorTest { val item = AmmoBox(GlobalDefinitions.bullet_9mm) val hub = new NumberPoolHub(new MaxNumberSource(20)) + val zone = new Zone( + "test", + new ZoneMap("test-map"), 0) { + override def SetupNumberPools() = {} + GUID(hub) + } hub.register(item, 10) - val zone = new Zone("test", new ZoneMap("test-map"), 0) { override def SetupNumberPools() = {} } - zone.GUID(hub) zone.actor = system.spawn(ZoneActor(zone), ZoneTest.TestName) expectNoMessage(200 milliseconds) @@ -622,9 +642,13 @@ class ZoneGroundPickupItemTest extends ActorTest { class ZoneGroundCanNotPickupItemTest extends ActorTest { val item = AmmoBox(GlobalDefinitions.bullet_9mm) val hub = new NumberPoolHub(new MaxNumberSource(20)) + val zone = new Zone( + "test", + new ZoneMap("test-map"), 0) { + override def SetupNumberPools() = {} + GUID(hub) + } hub.register(item, 10) - val zone = new Zone("test", new ZoneMap("test-map"), 0) { override def SetupNumberPools() = {} } - zone.GUID(hub) //still registered to this zone zone.actor = system.spawn(ZoneActor(zone), ZoneTest.TestName) expectNoMessage(200 milliseconds) @@ -646,9 +670,13 @@ class ZoneGroundCanNotPickupItemTest extends ActorTest { class ZoneGroundRemoveItemTest extends ActorTest { val item = AmmoBox(GlobalDefinitions.bullet_9mm) val hub = new NumberPoolHub(new MaxNumberSource(20)) + val zone = new Zone( + "test", + new ZoneMap("test-map"), 0) { + override def SetupNumberPools() = {} + GUID(hub) + } hub.register(item, 10) - val zone = new Zone("test", new ZoneMap("test-map"), 0) { override def SetupNumberPools() = {} } - zone.GUID(hub) //still registered to this zone zone.actor = system.spawn(ZoneActor(zone), ZoneTest.TestName) expectNoMessage(200 milliseconds) diff --git a/src/test/scala/objects/terminal/ImplantTerminalMechTest.scala b/src/test/scala/objects/terminal/ImplantTerminalMechTest.scala index 1f9712356..abe84021b 100644 --- a/src/test/scala/objects/terminal/ImplantTerminalMechTest.scala +++ b/src/test/scala/objects/terminal/ImplantTerminalMechTest.scala @@ -161,31 +161,37 @@ object ImplantTerminalMechTest { import akka.actor.typed.scaladsl.adapter._ val guid = new NumberPoolHub(new MaxNumberSource(10)) - val map = new ZoneMap("test") - val zone = new Zone("test", map, 0) { - override def SetupNumberPools() = {} - GUID(guid) - this.actor = new TestProbe(system).ref.toTyped[ZoneActor.Command] - } + val terminal = ImplantTerminalMech(GlobalDefinitions.implant_terminal_mech) //guid=1 + val interface = Terminal(GlobalDefinitions.implant_terminal_interface) //guid=2 val building = new Building( "Building", building_guid = 0, map_id = 0, - zone, + Zone.Nowhere, StructureType.Building, GlobalDefinitions.building ) //guid=3 + val zone = new Zone( + "test", + new ZoneMap("test") { + }, + 0) { + override def SetupNumberPools() = {} + GUID(guid) + this.actor = new TestProbe(system).ref.toTyped[ZoneActor.Command] + } + //zone.actor = system.spawn(ZoneActor(zone), ZoneTest.TestName) + zone.map.linkTerminalToInterface(1, 2) + building.Zone = zone building.Faction = faction - - val interface = Terminal(GlobalDefinitions.implant_terminal_interface) //guid=2 - interface.Owner = building - val terminal = ImplantTerminalMech(GlobalDefinitions.implant_terminal_mech) //guid=1 - terminal.Owner = building + interface.Zone = zone + building.Amenities = interface + terminal.Zone = zone + building.Amenities = terminal guid.register(terminal, 1) guid.register(interface, 2) guid.register(building, 3) - map.linkTerminalToInterface(1, 2) terminal.Actor = system.actorOf(Props(classOf[ImplantTerminalMechControl], terminal), "terminal-control") (Player(Avatar(0, "test", faction, CharacterSex.Male, 0, CharacterVoice.Mute)), terminal)