diff --git a/common/src/main/scala/net/psforever/objects/GlobalDefinitions.scala b/common/src/main/scala/net/psforever/objects/GlobalDefinitions.scala index 52843e786..1595fb780 100644 --- a/common/src/main/scala/net/psforever/objects/GlobalDefinitions.scala +++ b/common/src/main/scala/net/psforever/objects/GlobalDefinitions.scala @@ -13,6 +13,7 @@ import net.psforever.objects.serverobject.mblocker.LockerDefinition import net.psforever.objects.serverobject.pad.VehicleSpawnPadDefinition import net.psforever.objects.serverobject.terminals._ import net.psforever.objects.serverobject.tube.SpawnTubeDefinition +import net.psforever.objects.serverobject.resourcesilo.ResourceSiloDefinition import net.psforever.objects.vehicles.{SeatArmorRestriction, UtilityType} import net.psforever.types.PlanetSideEmpire @@ -563,6 +564,8 @@ object GlobalDefinitions { val door = new DoorDefinition + val resource_silo = new ResourceSiloDefinition + /** * Given a faction, provide the standard assault melee weapon. * @param faction the faction diff --git a/common/src/main/scala/net/psforever/objects/serverobject/resourcesilo/ResourceSilo.scala b/common/src/main/scala/net/psforever/objects/serverobject/resourcesilo/ResourceSilo.scala new file mode 100644 index 000000000..9e653c316 --- /dev/null +++ b/common/src/main/scala/net/psforever/objects/serverobject/resourcesilo/ResourceSilo.scala @@ -0,0 +1,86 @@ +// Copyright (c) 2017 PSForever +package net.psforever.objects.serverobject.resourcesilo + +import akka.actor.{ActorContext, Props} +import net.psforever.objects.{GlobalDefinitions, Player, Vehicle} +import net.psforever.objects.serverobject.structures.Amenity +import net.psforever.packet.game.UseItemMessage + +class ResourceSilo extends Amenity { + private var chargeLevel : Int = 0 + private val maximumCharge : Int = 1000 + // For the flashing red light on top of the NTU silo on + private var lowNtuWarningOn : Int = 0 + + // For the NTU display bar + private var capacitorDisplay : Long = 0 + + def ChargeLevel : Int = chargeLevel + + // Do not call directly. Use ResourceSilo.UpdateChargeLevel message to handle logic such as low ntu warnings + def ChargeLevel_=(charge: Int) : Int = { + if(charge < 0 ) { + chargeLevel = 0 + } else if (charge > maximumCharge) { + chargeLevel = maximumCharge + } else { + chargeLevel = charge + } + ChargeLevel + } + + def MaximumCharge : Int = maximumCharge + + def LowNtuWarningOn : Int = lowNtuWarningOn + def LowNtuWarningOn_=(enabled: Int) : Int = { + lowNtuWarningOn = enabled + LowNtuWarningOn + } + + def CapacitorDisplay : Long = capacitorDisplay + def CapacitorDisplay_=(value: Long) : Long = { + capacitorDisplay = value + CapacitorDisplay + } + + def Definition : ResourceSiloDefinition = GlobalDefinitions.resource_silo + + def Use(player: Player, msg : UseItemMessage) : ResourceSilo.Exchange = { + ResourceSilo.ChargeEvent() + } +} + + +object ResourceSilo { + + final case class Use(player: Player, msg : UseItemMessage) + final case class UpdateChargeLevel(amount: Int) + final case class LowNtuWarning(enabled: Int) + final case class SyncStateWithClient() + sealed trait Exchange + final case class ChargeEvent() extends Exchange + final case class ResourceSiloMessage(player: Player, msg : UseItemMessage, response : Exchange) + + + /** + * Overloaded constructor. + * @return the `Resource Silo` object + */ + def apply() : ResourceSilo = { + new ResourceSilo() + } + + /** + * Instantiate an configure a `Resource Silo` object + * @param id the unique id that will be assigned to this entity + * @param context a context to allow the object to properly set up `ActorSystem` functionality; + * not necessary for this object, but required by signature + * @return the `Locker` object + */ + def Constructor(id : Int, context : ActorContext) : ResourceSilo = { + val obj = ResourceSilo() + obj.Actor = context.actorOf(Props(classOf[ResourceSiloControl], obj), s"${obj.Definition.Name}_$id") + obj.Actor ! "startup" + obj + } +} \ No newline at end of file diff --git a/common/src/main/scala/net/psforever/objects/serverobject/resourcesilo/ResourceSiloControl.scala b/common/src/main/scala/net/psforever/objects/serverobject/resourcesilo/ResourceSiloControl.scala new file mode 100644 index 000000000..18e38fde9 --- /dev/null +++ b/common/src/main/scala/net/psforever/objects/serverobject/resourcesilo/ResourceSiloControl.scala @@ -0,0 +1,80 @@ +// Copyright (c) 2017 PSForever +package net.psforever.objects.serverobject.resourcesilo + +import akka.actor.{Actor, ActorRef} +import net.psforever.objects.serverobject.affinity.{FactionAffinity, FactionAffinityBehavior} +import net.psforever.objects.serverobject.structures.Building +import net.psforever.packet.game.PlanetSideGUID +import services.ServiceManager.Lookup +import services._ +import services.avatar.{AvatarAction, AvatarServiceMessage} + +import scala.concurrent.ExecutionContext.Implicits.global +import scala.concurrent.duration._ + + +/** + * An `Actor` that handles messages being dispatched to a specific `Resource Silo`. + * @param resourceSilo the `Resource Silo` object being governed + */ +class ResourceSiloControl(resourceSilo : ResourceSilo) extends Actor with FactionAffinityBehavior.Check { + def FactionObject : FactionAffinity = resourceSilo + var avatarService : ActorRef = Actor.noSender + private[this] val log = org.log4s.getLogger + + def receive : Receive = { + case "startup" => + ServiceManager.serviceManager ! Lookup("avatar") //ask for a resolver to deal with the GUID system + + case ServiceManager.LookupResult("avatar", endpoint) => + avatarService = endpoint + log.info("ResourceSiloControl: Silo " + resourceSilo.GUID + " Got avatar service " + endpoint) + + // todo: This is just a temporary solution to drain NTU over time. When base object destruction is properly implemented NTU should be deducted when base objects repair themselves + context.system.scheduler.schedule(5 second, 5 second, self, ResourceSilo.UpdateChargeLevel(-1)) + context.become(Processing) + + case _ => ; + } + + def Processing : Receive = checkBehavior.orElse { + case ResourceSilo.Use(player, msg) => + sender ! ResourceSilo.ResourceSiloMessage(player, msg, resourceSilo.Use(player, msg)) + case ResourceSilo.LowNtuWarning(enabled: Int) => + resourceSilo.LowNtuWarningOn = enabled + log.trace(s"LowNtuWarning: Silo ${resourceSilo.GUID} low ntu warning set to ${enabled}") + avatarService ! AvatarServiceMessage(resourceSilo.Owner.asInstanceOf[Building].Zone.Id, AvatarAction.PlanetsideAttribute(PlanetSideGUID(resourceSilo.Owner.asInstanceOf[Building].ModelId), 47, resourceSilo.LowNtuWarningOn)) + + case ResourceSilo.UpdateChargeLevel(amount: Int) => + // Increase if positive passed in or decrease charge level if negative number is passed in + resourceSilo.ChargeLevel += amount + if(resourceSilo.ChargeLevel > 0) { + log.trace(s"UpdateChargeLevel: Silo ${resourceSilo.GUID} set to ${resourceSilo.ChargeLevel}") + } + + val ntuIsLow = resourceSilo.ChargeLevel.toFloat / resourceSilo.MaximumCharge.toFloat < 0.2f + + val ntuBarLevel = scala.math.round((resourceSilo.ChargeLevel.toFloat / resourceSilo.MaximumCharge.toFloat) * 10).toInt + // Only send updated capacitor display value to all clients if it has actually changed + if(resourceSilo.CapacitorDisplay != ntuBarLevel) { + log.trace(s"Silo ${resourceSilo.GUID} NTU bar level has changed from ${resourceSilo.CapacitorDisplay} to ${ntuBarLevel}") + resourceSilo.CapacitorDisplay = ntuBarLevel + avatarService ! AvatarServiceMessage(resourceSilo.Owner.asInstanceOf[Building].Zone.Id, AvatarAction.PlanetsideAttribute(resourceSilo.GUID, 45, resourceSilo.CapacitorDisplay)) + } + + if(resourceSilo.LowNtuWarningOn == 1 && !ntuIsLow){ + self ! ResourceSilo.LowNtuWarning(0) + } else if (resourceSilo.LowNtuWarningOn == 0 && ntuIsLow) { + self ! ResourceSilo.LowNtuWarning(1) + } + + //todo: Shut down base power and make base neutral if silo hits zero NTU + case ResourceSilo.SyncStateWithClient() => + avatarService ! AvatarServiceMessage(resourceSilo.Owner.asInstanceOf[Building].Zone.Id, AvatarAction.PlanetsideAttribute(PlanetSideGUID(resourceSilo.Owner.asInstanceOf[Building].ModelId), 47, resourceSilo.LowNtuWarningOn)) + avatarService ! AvatarServiceMessage(resourceSilo.Owner.asInstanceOf[Building].Zone.Id, AvatarAction.PlanetsideAttribute(resourceSilo.GUID, 45, resourceSilo.CapacitorDisplay)) + + case _ => ; + } + + +} diff --git a/common/src/main/scala/net/psforever/objects/serverobject/resourcesilo/ResourceSiloDefinition.scala b/common/src/main/scala/net/psforever/objects/serverobject/resourcesilo/ResourceSiloDefinition.scala new file mode 100644 index 000000000..1f5570622 --- /dev/null +++ b/common/src/main/scala/net/psforever/objects/serverobject/resourcesilo/ResourceSiloDefinition.scala @@ -0,0 +1,12 @@ +// Copyright (c) 2017 PSForever +package net.psforever.objects.serverobject.resourcesilo + +import net.psforever.objects.definition.ObjectDefinition + +/** + * The definition for any `Resource Silo`. + * Object Id 731. + */ +class ResourceSiloDefinition extends ObjectDefinition(731) { + Name = "resource_silo" +}