diff --git a/src/main/scala/net/psforever/actors/session/AvatarActor.scala b/src/main/scala/net/psforever/actors/session/AvatarActor.scala index c04691e92..e876ddda6 100644 --- a/src/main/scala/net/psforever/actors/session/AvatarActor.scala +++ b/src/main/scala/net/psforever/actors/session/AvatarActor.scala @@ -913,43 +913,8 @@ class AvatarActor( AvatarActor.displayLookingForSquad(session.get, if (lfs) 1 else 0) Behaviors.same - case CreateAvatar(name, head, voice, gender, empire) => - import ctx._ - ctx.run(query[persistence.Avatar].filter(_.name ilike lift(name)).filter(!_.deleted)).onComplete { - case Success(characters) => - characters.headOption match { - case None => - val result = for { - _ <- ctx.run( - query[persistence.Avatar] - .insert( - _.name -> lift(name), - _.accountId -> lift(account.id), - _.factionId -> lift(empire.id), - _.headId -> lift(head), - _.voiceId -> lift(voice.id), - _.genderId -> lift(gender.value), - _.bep -> lift(Config.app.game.newAvatar.br.experience), - _.cep -> lift(Config.app.game.newAvatar.cr.experience) - ) - ) - } yield () - result.onComplete { - case Success(_) => - log.debug(s"AvatarActor: created character $name for account ${account.name}") - sessionActor ! SessionActor.SendResponse(ActionResultMessage.Pass) - sendAvatars(account) - case Failure(e) => log.error(e)("db failure") - } - case Some(_) => - // send "char already exists" - sessionActor ! SessionActor.SendResponse(ActionResultMessage.Fail(1)) - } - case Failure(e) => - log.error(e)("db failure") - sessionActor ! SessionActor.SendResponse(ActionResultMessage.Fail(3)) - sendAvatars(account) - } + case CreateAvatar(name, head, voice, sex, empire) => + createAvatar(account, name, sex, empire, head, voice) Behaviors.same case DeleteAvatar(id) => @@ -2999,4 +2964,114 @@ class AvatarActor( def updateToolDischarge(stats: EquipmentStat): Unit = { avatar.scorecard.rate(stats) } + + def createAvatar( + account: Account, + name: String, + sex: CharacterSex, + empire: PlanetSideEmpire.Value, + head: Int, + voice: CharacterVoice.Value + ): Unit = { + import ctx._ + ctx.run( + query[persistence.Avatar] + .filter(_.name ilike lift(name)) + .map(a => (a.accountId, a.deleted, a.created)) + ) + .onComplete { + case Success(foundCharacters) if foundCharacters.size == 1 => + testFoundCharacters( + account, + name, + foundCharacters + .collect { case (a, b, c) => (a, b, c.toDate.getTime) } + ) + case Success(foundCharacters) if foundCharacters.nonEmpty => + testFoundCharacters( + account, + name, + foundCharacters + .map { case (a, b, created) => (a, b, created.toDate.getTime) } + .sortBy { case (_, _, created) => created } + .toList + ) + case Success(_) => + //create new character + actuallyCreateNewCharacter(account.id, account.name, name, sex, empire, head, voice) + sendAvatars(account) + case Failure(e) => + log.error(e)("db failure") + sessionActor ! SessionActor.SendResponse(ActionResultMessage.Fail(error = 3)) + sendAvatars(account) + } + } + + private def testFoundCharacters( + account: Account, + name: String, + foundCharacters: IterableOnce[(Int, Boolean, Long)]): Unit = { + val foundCharactersIterator = foundCharacters.iterator + if (foundCharactersIterator.exists { case (_, deleted, _ ) => !deleted } || + foundCharactersIterator.exists { case (accountId, _, _) => accountId != account.id }) { + //send "char already exists" + sessionActor ! SessionActor.SendResponse(ActionResultMessage.Fail(error = 1)) + } else { + //reactivate character + reactivateCharacter(account.id, account.name, name) + sendAvatars(account) + } + } + + private def actuallyCreateNewCharacter( + accountId: Int, + accountName: String, + name: String, + sex: CharacterSex, + empire: PlanetSideEmpire.Value, + head: Int, + voice: CharacterVoice.Value + ): Unit = { + import ctx._ + val result = for { + _ <- ctx.run( + query[persistence.Avatar] + .insert( + _.name -> lift(name), + _.accountId -> lift(accountId), + _.factionId -> lift(empire.id), + _.headId -> lift(head), + _.voiceId -> lift(voice.id), + _.genderId -> lift(sex.value), + _.bep -> lift(Config.app.game.newAvatar.br.experience), + _.cep -> lift(Config.app.game.newAvatar.cr.experience) + ) + ) + } yield () + result.onComplete { + case Success(_) => + log.debug(s"AvatarActor: created character $name for account $accountName") + sessionActor ! SessionActor.SendResponse(ActionResultMessage.Pass) + case Failure(e) => + log.error(e)("db failure") + sessionActor ! SessionActor.SendResponse(ActionResultMessage.Fail(error = 3)) + } + } + + private def reactivateCharacter(accountId: Int, accountName: String, characterName: String): Unit = { + import ctx._ + ctx.run( + query[persistence.Avatar] + .filter(a => a.accountId == lift(accountId)) + .filter(a => a.name ilike lift(characterName)) + .update(_.deleted -> lift(false)) + ).onComplete { + case Success(_) => + log.debug(s"AvatarActor: character belonging to $accountName with name $characterName reactivated") + sessionActor ! SessionActor.SendResponse(ActionResultMessage.Pass) + case Failure(e) => + log.error(e)("db failure") + sessionActor ! SessionActor.SendResponse(ActionResultMessage.Fail(error = 3)) + } + } } diff --git a/src/main/scala/net/psforever/actors/session/support/SessionMountHandlers.scala b/src/main/scala/net/psforever/actors/session/support/SessionMountHandlers.scala index 23d18afb9..af7dac261 100644 --- a/src/main/scala/net/psforever/actors/session/support/SessionMountHandlers.scala +++ b/src/main/scala/net/psforever/actors/session/support/SessionMountHandlers.scala @@ -56,6 +56,7 @@ class SessionMountHandlers( sendResponse(PlanetsideAttributeMessage(obj_guid, obj.Definition.shieldUiAttribute, obj.Shields)) sendResponse(PlanetsideAttributeMessage(obj_guid, attribute_type=45, obj.NtuCapacitorScaled)) sendResponse(GenericObjectActionMessage(obj_guid, code=11)) + sessionData.accessContainer(obj) MountingAction(tplayer, obj, seatNumber) case Mountable.CanMount(obj: Vehicle, seatNumber, _) diff --git a/src/main/scala/net/psforever/actors/session/support/VehicleOperations.scala b/src/main/scala/net/psforever/actors/session/support/VehicleOperations.scala index ecf6dc8bf..0285d72f2 100644 --- a/src/main/scala/net/psforever/actors/session/support/VehicleOperations.scala +++ b/src/main/scala/net/psforever/actors/session/support/VehicleOperations.scala @@ -279,11 +279,9 @@ class VehicleOperations( def handleDismountVehicle(pkt: DismountVehicleMsg): Unit = { val DismountVehicleMsg(player_guid, bailType, wasKickedByDriver) = pkt + val dError: (String, Player)=> Unit = dismountError(bailType, wasKickedByDriver) //TODO optimize this later //common warning for this section - def dismountWarning(note: String): Unit = { - log.error(s"$note; some vehicle might not know that ${player.Name} is no longer sitting in it") - } if (player.GUID == player_guid) { //normally disembarking from a mount (sessionData.zoning.interstellarFerry.orElse(continent.GUID(player.VehicleSeated)) match { @@ -295,10 +293,7 @@ class VehicleOperations( case out @ Some(_: Mountable) => out case _ => - dismountWarning( - s"DismountVehicleMsg: player ${player.Name}_guid not considered seated in a mountable entity" - ) - sendResponse(DismountVehicleMsg(player_guid, bailType, wasKickedByDriver)) + dError(s"DismountVehicleMsg: player ${player.Name} not considered seated in a mountable entity", player) None }) match { case Some(_) if serverVehicleControlVelocity.nonEmpty => @@ -325,15 +320,14 @@ class VehicleOperations( } case None => - dismountWarning( - s"DismountVehicleMsg: can not find where player ${player.Name}_guid is seated in mountable ${player.VehicleSeated}" - ) + dError(s"DismountVehicleMsg: can not find where player ${player.Name}_guid is seated in mountable ${player.VehicleSeated}", player) } case _ => - dismountWarning(s"DismountVehicleMsg: can not find mountable entity ${player.VehicleSeated}") + dError(s"DismountVehicleMsg: can not find mountable entity ${player.VehicleSeated}", player) } } else { //kicking someone else out of a mount; need to own that mount/mountable + val dWarn: (String, Player)=> Unit = dismountWarning(bailType, wasKickedByDriver) player.avatar.vehicle match { case Some(obj_guid) => ( @@ -353,23 +347,47 @@ class VehicleOperations( case Some(seat_num) => obj.Actor ! Mountable.TryDismount(tplayer, seat_num, bailType) case None => - dismountWarning( - s"DismountVehicleMsg: can not find where other player ${player.Name}_guid is seated in mountable $obj_guid" - ) + dError(s"DismountVehicleMsg: can not find where other player ${tplayer.Name} is seated in mountable $obj_guid", tplayer) } - case (None, _) => ; - log.warn(s"DismountVehicleMsg: ${player.Name} can not find his vehicle") - case (_, None) => ; - log.warn(s"DismountVehicleMsg: player $player_guid could not be found to kick, ${player.Name}") + case (None, _) => + dWarn(s"DismountVehicleMsg: ${player.Name} can not find his vehicle", player) + case (_, None) => + dWarn(s"DismountVehicleMsg: player $player_guid could not be found to kick, ${player.Name}", player) case _ => - log.warn(s"DismountVehicleMsg: object is either not a Mountable or not a Player") + dWarn(s"DismountVehicleMsg: object is either not a Mountable or not a Player", player) } case None => - log.warn(s"DismountVehicleMsg: ${player.Name} does not own a vehicle") + dWarn(s"DismountVehicleMsg: ${player.Name} does not own a vehicle", player) } } } + private def dismountWarning( + bailAs: BailType.Value, + kickedByDriver: Boolean + ) + ( + note: String, + player: Player + ): Unit = { + log.warn(note) + player.VehicleSeated = None + sendResponse(DismountVehicleMsg(player.GUID, bailAs, kickedByDriver)) + } + + private def dismountError( + bailAs: BailType.Value, + kickedByDriver: Boolean + ) + ( + note: String, + player: Player + ): Unit = { + log.error(s"$note; some vehicle might not know that ${player.Name} is no longer sitting in it") + player.VehicleSeated = None + sendResponse(DismountVehicleMsg(player.GUID, bailAs, kickedByDriver)) + } + def handleMountVehicleCargo(pkt: MountVehicleCargoMsg): Unit = { val MountVehicleCargoMsg(_, cargo_guid, carrier_guid, _) = pkt (continent.GUID(cargo_guid), continent.GUID(carrier_guid)) match { diff --git a/src/main/scala/net/psforever/objects/Deployables.scala b/src/main/scala/net/psforever/objects/Deployables.scala index 596a96fb0..085eaef4b 100644 --- a/src/main/scala/net/psforever/objects/Deployables.scala +++ b/src/main/scala/net/psforever/objects/Deployables.scala @@ -82,19 +82,16 @@ object Deployables { val zone = target.Zone val events = zone.LocalEvents val item = target.Definition.Item - target.OwnerName match { - case Some(owner) => - zone.Players.find { p => owner.equals(p.name) } match { - case Some(p) => - if (p.deployables.Remove(target)) { - events ! LocalServiceMessage(owner, LocalAction.DeployableUIFor(item)) - } - case None => ; - } + target.OwnerName + .foreach { owner => + zone.LivePlayers.find { p => owner.equals(p.Name) } + .orElse(zone.Corpses.find { c => owner.equals(c.Name) }) + .foreach { p => + p.Actor ! Player.LoseDeployable(target) + } target.Owner = None target.OwnerName = None - case None => ; - } + } events ! LocalServiceMessage( s"${target.Faction}", LocalAction.DeployableMapIcon( @@ -114,6 +111,7 @@ object Deployables { * @return all previously-owned deployables after they have been processed; * boomers are listed before all other deployable types */ + //noinspection ScalaUnusedSymbol def Disown(zone: Zone, avatar: Avatar, replyTo: ActorRef): List[Deployable] = { avatar.deployables .Clear() diff --git a/src/main/scala/net/psforever/objects/avatar/PlayerControl.scala b/src/main/scala/net/psforever/objects/avatar/PlayerControl.scala index c8d32b20a..eb2da472f 100644 --- a/src/main/scala/net/psforever/objects/avatar/PlayerControl.scala +++ b/src/main/scala/net/psforever/objects/avatar/PlayerControl.scala @@ -802,7 +802,7 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm //choose if (target.Health > 0) { //alive - if (target.Health <= 25 && target.Health + damageToHealth > 25 && + if (target.Health <= 25 && (player.avatar.implants.flatten.find { _.definition.implantType == ImplantType.SecondWind } match { case Some(wind) => wind.initialized case _ => false @@ -810,8 +810,8 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm //activate second wind player.Health += 25 player.LogActivity(HealFromImplant(ImplantType.SecondWind, 25)) - avatarActor ! AvatarActor.ResetImplant(ImplantType.SecondWind) avatarActor ! AvatarActor.RestoreStamina(25) + avatarActor ! AvatarActor.ResetImplant(ImplantType.SecondWind) } //take damage/update DamageAwareness(target, cause, damageToHealth, damageToArmor, damageToStamina, damageToCapacitor) diff --git a/src/main/scala/net/psforever/objects/vehicles/AntTransferBehavior.scala b/src/main/scala/net/psforever/objects/vehicles/AntTransferBehavior.scala index 5a948cd93..d1655265c 100644 --- a/src/main/scala/net/psforever/objects/vehicles/AntTransferBehavior.scala +++ b/src/main/scala/net/psforever/objects/vehicles/AntTransferBehavior.scala @@ -50,7 +50,7 @@ trait AntTransferBehavior extends TransferBehavior with NtuStorageBehavior { def UpdateNtuUI(vehicle: Vehicle with NtuContainer): Unit = { if (vehicle.Seats.values.exists(_.isOccupied)) { - val display = scala.math.ceil(vehicle.NtuCapacitorScaled).toLong + val display = vehicle.NtuCapacitorScaled.toLong vehicle.Zone.VehicleEvents ! VehicleServiceMessage( vehicle.Actor.toString, VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, vehicle.GUID, 45, display) diff --git a/src/main/scala/net/psforever/objects/zones/blockmap/Sector.scala b/src/main/scala/net/psforever/objects/zones/blockmap/Sector.scala index 126f7d35b..ae2e3373d 100644 --- a/src/main/scala/net/psforever/objects/zones/blockmap/Sector.scala +++ b/src/main/scala/net/psforever/objects/zones/blockmap/Sector.scala @@ -69,7 +69,7 @@ trait SectorTraits { * @param eqFunc a custom equivalence function to distinguish between the entities in the list * @tparam A the type of object that will be the entities stored in the list */ -class SectorListOf[A](eqFunc: (A, A) => Boolean = (a: A, b: A) => a equals b) { +class SectorListOf[A](eqFunc: (A, A) => Boolean = (a: A, b: A) => a == b) { private val internalList: ListBuffer[A] = ListBuffer[A]() /**