mirror of
https://github.com/2revoemag/PSF-BotServer.git
synced 2026-07-13 15:34:42 +00:00
No Uniform, No Helmet, No Service (#1040)
* added extra checks to eliminate cosmetics from the packet transcoder where having them defined would be considered harmful to the data * new uniform options; moved cosmetics class file * assurance that the cosmetics settings are accurate during transitory points
This commit is contained in:
parent
fdcce870d9
commit
7e899e9ef3
11 changed files with 139 additions and 71 deletions
|
|
@ -24,7 +24,6 @@ import net.psforever.objects.avatar.{
|
||||||
BattleRank,
|
BattleRank,
|
||||||
Certification,
|
Certification,
|
||||||
Cooldowns,
|
Cooldowns,
|
||||||
Cosmetic,
|
|
||||||
Friend => AvatarFriend,
|
Friend => AvatarFriend,
|
||||||
Ignored => AvatarIgnored,
|
Ignored => AvatarIgnored,
|
||||||
Implant,
|
Implant,
|
||||||
|
|
@ -45,8 +44,9 @@ import net.psforever.objects.vital.HealFromImplant
|
||||||
import net.psforever.packet.game.objectcreate.{ObjectClass, RibbonBars}
|
import net.psforever.packet.game.objectcreate.{ObjectClass, RibbonBars}
|
||||||
import net.psforever.packet.game.{Friend => GameFriend, _}
|
import net.psforever.packet.game.{Friend => GameFriend, _}
|
||||||
import net.psforever.types.{
|
import net.psforever.types.{
|
||||||
CharacterVoice,
|
|
||||||
CharacterSex,
|
CharacterSex,
|
||||||
|
CharacterVoice,
|
||||||
|
Cosmetic,
|
||||||
ExoSuitType,
|
ExoSuitType,
|
||||||
ImplantType,
|
ImplantType,
|
||||||
LoadoutType,
|
LoadoutType,
|
||||||
|
|
@ -806,7 +806,13 @@ object AvatarActor {
|
||||||
out.future
|
out.future
|
||||||
}
|
}
|
||||||
|
|
||||||
def toAvatar(avatar: persistence.Avatar): Avatar =
|
def toAvatar(avatar: persistence.Avatar): Avatar = {
|
||||||
|
val bep = avatar.bep
|
||||||
|
val convertedCosmetics = if (BattleRank.showCosmetics(bep)) {
|
||||||
|
avatar.cosmetics.map(c => Cosmetic.valuesFromObjectCreateValue(c))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
Avatar(
|
Avatar(
|
||||||
avatar.id,
|
avatar.id,
|
||||||
BasicCharacterData(
|
BasicCharacterData(
|
||||||
|
|
@ -816,10 +822,11 @@ object AvatarActor {
|
||||||
avatar.headId,
|
avatar.headId,
|
||||||
CharacterVoice(avatar.voiceId)
|
CharacterVoice(avatar.voiceId)
|
||||||
),
|
),
|
||||||
avatar.bep,
|
bep,
|
||||||
avatar.cep,
|
avatar.cep,
|
||||||
decoration = ProgressDecoration(cosmetics = avatar.cosmetics.map(c => Cosmetic.valuesFromObjectCreateValue(c)))
|
decoration = ProgressDecoration(cosmetics = convertedCosmetics)
|
||||||
)
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class AvatarActor(
|
class AvatarActor(
|
||||||
|
|
@ -936,6 +943,7 @@ class AvatarActor(
|
||||||
case DeleteAvatar(id) =>
|
case DeleteAvatar(id) =>
|
||||||
import ctx._
|
import ctx._
|
||||||
val performDeletion = for {
|
val performDeletion = for {
|
||||||
|
_ <- ctx.run(query[persistence.Shortcut].filter(_.avatarId == lift(id)).delete)
|
||||||
_ <- ctx.run(query[persistence.Implant].filter(_.avatarId == lift(id)).delete)
|
_ <- ctx.run(query[persistence.Implant].filter(_.avatarId == lift(id)).delete)
|
||||||
_ <- ctx.run(query[persistence.Loadout].filter(_.avatarId == lift(id)).delete)
|
_ <- ctx.run(query[persistence.Loadout].filter(_.avatarId == lift(id)).delete)
|
||||||
_ <- ctx.run(query[persistence.Locker].filter(_.avatarId == lift(id)).delete)
|
_ <- ctx.run(query[persistence.Locker].filter(_.avatarId == lift(id)).delete)
|
||||||
|
|
@ -1886,9 +1894,10 @@ class AvatarActor(
|
||||||
)
|
)
|
||||||
.onComplete {
|
.onComplete {
|
||||||
case Success(_) =>
|
case Success(_) =>
|
||||||
|
val zone = session.get.zone
|
||||||
avatarCopy(avatar.copy(decoration = avatar.decoration.copy(cosmetics = Some(cosmetics))))
|
avatarCopy(avatar.copy(decoration = avatar.decoration.copy(cosmetics = Some(cosmetics))))
|
||||||
session.get.zone.AvatarEvents ! AvatarServiceMessage(
|
zone.AvatarEvents ! AvatarServiceMessage(
|
||||||
session.get.zone.id,
|
zone.id,
|
||||||
AvatarAction.PlanetsideAttributeToAll(
|
AvatarAction.PlanetsideAttributeToAll(
|
||||||
session.get.player.GUID,
|
session.get.player.GUID,
|
||||||
106,
|
106,
|
||||||
|
|
@ -2816,23 +2825,31 @@ class AvatarActor(
|
||||||
|
|
||||||
def setBep(bep: Long, modifier: ExperienceType): Unit = {
|
def setBep(bep: Long, modifier: ExperienceType): Unit = {
|
||||||
import ctx._
|
import ctx._
|
||||||
|
val current = BattleRank.withExperience(avatar.bep).value
|
||||||
|
val next = BattleRank.withExperience(bep).value
|
||||||
|
lazy val br24 = BattleRank.BR24.value
|
||||||
val result = for {
|
val result = for {
|
||||||
_ <-
|
|
||||||
if (BattleRank.withExperience(bep).value < BattleRank.BR24.value) setCosmetics(Set())
|
|
||||||
else Future.successful(())
|
|
||||||
r <- ctx.run(query[persistence.Avatar].filter(_.id == lift(avatar.id)).update(_.bep -> lift(bep)))
|
r <- ctx.run(query[persistence.Avatar].filter(_.id == lift(avatar.id)).update(_.bep -> lift(bep)))
|
||||||
} yield r
|
} yield r
|
||||||
result.onComplete {
|
result.onComplete {
|
||||||
case Success(_) =>
|
case Success(_) =>
|
||||||
val sess = session.get
|
val sess = session.get
|
||||||
val zone = sess.zone
|
val zone = sess.zone
|
||||||
val pguid = sess.player.GUID
|
val zoneId = zone.id
|
||||||
|
val events = zone.AvatarEvents
|
||||||
|
val player = sess.player
|
||||||
|
val pguid = player.GUID
|
||||||
val localModifier = modifier
|
val localModifier = modifier
|
||||||
sessionActor ! SessionActor.SendResponse(BattleExperienceMessage(pguid, bep, localModifier))
|
sessionActor ! SessionActor.SendResponse(BattleExperienceMessage(pguid, bep, localModifier))
|
||||||
zone.AvatarEvents ! AvatarServiceMessage(
|
events ! AvatarServiceMessage(zoneId, AvatarAction.PlanetsideAttributeToAll(pguid, 17, bep))
|
||||||
zone.id,
|
if (current < br24 && next >= br24 || current >= br24 && next < br24) {
|
||||||
AvatarAction.PlanetsideAttributeToAll(pguid, 17, bep)
|
setCosmetics(Set()).onComplete { _ =>
|
||||||
)
|
val evts = events
|
||||||
|
val name = player.Name
|
||||||
|
val guid = pguid
|
||||||
|
evts ! AvatarServiceMessage(name, AvatarAction.PlanetsideAttributeToAll(guid, 106, 1)) //set to no helmet
|
||||||
|
}
|
||||||
|
}
|
||||||
// when the level is reduced, take away any implants over the implant slot limit
|
// when the level is reduced, take away any implants over the implant slot limit
|
||||||
val implants = avatar.implants.zipWithIndex.map {
|
val implants = avatar.implants.zipWithIndex.map {
|
||||||
case (implant, index) =>
|
case (implant, index) =>
|
||||||
|
|
@ -2845,9 +2862,7 @@ class AvatarActor(
|
||||||
)
|
)
|
||||||
.onComplete {
|
.onComplete {
|
||||||
case Success(_) =>
|
case Success(_) =>
|
||||||
sessionActor ! SessionActor.SendResponse(
|
sessionActor ! SessionActor.SendResponse(AvatarImplantMessage(pguid, ImplantAction.Remove, index, 0))
|
||||||
AvatarImplantMessage(pguid, ImplantAction.Remove, index, 0)
|
|
||||||
)
|
|
||||||
case Failure(exception) =>
|
case Failure(exception) =>
|
||||||
log.error(exception)("db failure")
|
log.error(exception)("db failure")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ import scala.concurrent.duration._
|
||||||
import net.psforever.actors.zone.BuildingActor
|
import net.psforever.actors.zone.BuildingActor
|
||||||
import net.psforever.login.WorldSession
|
import net.psforever.login.WorldSession
|
||||||
import net.psforever.objects.{Default, Player, Session}
|
import net.psforever.objects.{Default, Player, Session}
|
||||||
import net.psforever.objects.avatar.{BattleRank, Certification, CommandRank, Cosmetic}
|
import net.psforever.objects.avatar.{BattleRank, Certification, CommandRank}
|
||||||
import net.psforever.objects.serverobject.pad.{VehicleSpawnControl, VehicleSpawnPad}
|
import net.psforever.objects.serverobject.pad.{VehicleSpawnControl, VehicleSpawnPad}
|
||||||
import net.psforever.objects.serverobject.resourcesilo.ResourceSilo
|
import net.psforever.objects.serverobject.resourcesilo.ResourceSilo
|
||||||
import net.psforever.objects.serverobject.structures.{Amenity, Building}
|
import net.psforever.objects.serverobject.structures.{Amenity, Building}
|
||||||
|
|
@ -30,7 +30,7 @@ import net.psforever.services.{CavernRotationService, InterstellarClusterService
|
||||||
import net.psforever.services.chat.ChatService
|
import net.psforever.services.chat.ChatService
|
||||||
import net.psforever.services.chat.ChatService.ChatChannel
|
import net.psforever.services.chat.ChatService.ChatChannel
|
||||||
import net.psforever.types.ChatMessageType.UNK_229
|
import net.psforever.types.ChatMessageType.UNK_229
|
||||||
import net.psforever.types.{ChatMessageType, PlanetSideEmpire, PlanetSideGUID, Vector3}
|
import net.psforever.types.{ChatMessageType, Cosmetic, PlanetSideEmpire, PlanetSideGUID, Vector3}
|
||||||
import net.psforever.util.{Config, PointOfInterest}
|
import net.psforever.util.{Config, PointOfInterest}
|
||||||
import net.psforever.zones.Zones
|
import net.psforever.zones.Zones
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
package net.psforever.objects.avatar
|
package net.psforever.objects.avatar
|
||||||
|
|
||||||
import enumeratum.values.{IntEnum, IntEnumEntry}
|
import enumeratum.values.{IntEnum, IntEnumEntry}
|
||||||
import net.psforever.packet.game.objectcreate.UniformStyle
|
import net.psforever.types.UniformStyle
|
||||||
|
|
||||||
/** Battle ranks and their starting experience values
|
/** Battle ranks and their starting experience values
|
||||||
* Source: http://wiki.psforever.net/wiki/Battle_Rank
|
* Source: http://wiki.psforever.net/wiki/Battle_Rank
|
||||||
|
|
@ -19,9 +19,11 @@ sealed abstract class BattleRank(val value: Int, val experience: Long) extends I
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def uniformStyle: UniformStyle.Value = {
|
def uniformStyle: UniformStyle = {
|
||||||
if (this.value >= BattleRank.BR25.value) {
|
if (this.value >= BattleRank.BR25.value) {
|
||||||
UniformStyle.ThirdUpgrade
|
UniformStyle.ThirdUpgrade
|
||||||
|
} else if (this.value == BattleRank.BR24.value) {
|
||||||
|
UniformStyle.SecondUpgradeBR24
|
||||||
} else if (this.value >= BattleRank.BR14.value) {
|
} else if (this.value >= BattleRank.BR14.value) {
|
||||||
UniformStyle.SecondUpgrade
|
UniformStyle.SecondUpgrade
|
||||||
} else if (this.value >= BattleRank.BR7.value) {
|
} else if (this.value >= BattleRank.BR7.value) {
|
||||||
|
|
@ -30,7 +32,6 @@ sealed abstract class BattleRank(val value: Int, val experience: Long) extends I
|
||||||
UniformStyle.Normal
|
UniformStyle.Normal
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case object BattleRank extends IntEnum[BattleRank] {
|
case object BattleRank extends IntEnum[BattleRank] {
|
||||||
|
|
@ -103,4 +104,26 @@ case object BattleRank extends IntEnum[BattleRank] {
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given a number of battle experience points,
|
||||||
|
* determine if the resulting battle rank is sufficient to display cosmetic details on a player character.
|
||||||
|
* @see `BattleRank.withExperience`
|
||||||
|
* @param bep amount of battle experience
|
||||||
|
* @return `true`, if cosmetic elements will be visible;
|
||||||
|
* `false`, otherwise
|
||||||
|
*/
|
||||||
|
def showCosmetics(bep: Long): Boolean = {
|
||||||
|
showCosmetics(BattleRank.withExperience(bep).uniformStyle)
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Given a certain level of uniform dress corresponding to battle rank,
|
||||||
|
* determine if the resulting battle rank is sufficient to display cosmetic details on a player character.
|
||||||
|
* @param uniform the style of uniform
|
||||||
|
* @return `true`, if cosmetic elements will be visible;
|
||||||
|
* `false`, otherwise
|
||||||
|
*/
|
||||||
|
def showCosmetics(uniform: UniformStyle): Boolean = {
|
||||||
|
uniform.value > UniformStyle.SecondUpgrade.value
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
package net.psforever.objects.definition.converter
|
package net.psforever.objects.definition.converter
|
||||||
|
|
||||||
import net.psforever.objects.Player
|
import net.psforever.objects.Player
|
||||||
|
import net.psforever.objects.avatar.BattleRank
|
||||||
import net.psforever.objects.equipment.{Equipment, EquipmentSlot}
|
import net.psforever.objects.equipment.{Equipment, EquipmentSlot}
|
||||||
import net.psforever.packet.game.objectcreate._
|
import net.psforever.packet.game.objectcreate._
|
||||||
import net.psforever.types.{ExoSuitType, GrenadeState, PlanetSideEmpire, PlanetSideGUID}
|
import net.psforever.types.{ExoSuitType, GrenadeState, PlanetSideEmpire, PlanetSideGUID}
|
||||||
|
|
@ -109,25 +110,41 @@ object AvatarConverter {
|
||||||
}
|
}
|
||||||
|
|
||||||
def MakeCharacterData(obj: Player): (Boolean, Boolean) => CharacterData = {
|
def MakeCharacterData(obj: Player): (Boolean, Boolean) => CharacterData = {
|
||||||
|
val avatar = obj.avatar
|
||||||
|
val uniformStyle = avatar.br.uniformStyle
|
||||||
|
val cosmetics = if (BattleRank.showCosmetics(uniformStyle)) {
|
||||||
|
avatar.decoration.cosmetics
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
val MaxArmor = obj.MaxArmor
|
val MaxArmor = obj.MaxArmor
|
||||||
|
val armor = if (MaxArmor == 0) {
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
StatConverter.Health(obj.Armor, MaxArmor)
|
||||||
|
}
|
||||||
CharacterData(
|
CharacterData(
|
||||||
StatConverter.Health(obj.Health, obj.MaxHealth),
|
StatConverter.Health(obj.Health, obj.MaxHealth),
|
||||||
if (MaxArmor == 0) {
|
armor,
|
||||||
0
|
uniformStyle,
|
||||||
} else {
|
|
||||||
StatConverter.Health(obj.Armor, MaxArmor)
|
|
||||||
},
|
|
||||||
obj.avatar.br.uniformStyle,
|
|
||||||
0,
|
0,
|
||||||
obj.avatar.cr.value,
|
avatar.cr.value,
|
||||||
obj.avatar.implants.flatten.filter(_.active).flatMap(_.definition.implantType.effect).toList,
|
avatar.implants.flatten.filter(_.active).flatMap(_.definition.implantType.effect).toList,
|
||||||
obj.avatar.decoration.cosmetics
|
cosmetics
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
def MakeDetailedCharacterData(obj: Player): Option[Int] => DetailedCharacterData = {
|
def MakeDetailedCharacterData(obj: Player): Option[Int] => DetailedCharacterData = {
|
||||||
val maxOpt: Option[Long] = if (obj.ExoSuit == ExoSuitType.MAX) { Some(0L) }
|
val maxOpt: Option[Long] = if (obj.ExoSuit == ExoSuitType.MAX) {
|
||||||
else { None }
|
Some(0L)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
val cosmetics = if (BattleRank.BR24.experience >= obj.avatar.bep) {
|
||||||
|
obj.avatar.decoration.cosmetics
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
val ba: DetailedCharacterA = DetailedCharacterA(
|
val ba: DetailedCharacterA = DetailedCharacterA(
|
||||||
obj.avatar.bep,
|
obj.avatar.bep,
|
||||||
obj.avatar.cep,
|
obj.avatar.cep,
|
||||||
|
|
@ -164,7 +181,7 @@ object AvatarConverter {
|
||||||
Nil,
|
Nil,
|
||||||
Nil,
|
Nil,
|
||||||
unkC = false,
|
unkC = false,
|
||||||
obj.avatar.decoration.cosmetics
|
cosmetics
|
||||||
)
|
)
|
||||||
pad_length: Option[Int] => DetailedCharacterData(ba, bb(obj.avatar.bep, pad_length))(pad_length)
|
pad_length: Option[Int] => DetailedCharacterData(ba, bb(obj.avatar.bep, pad_length))(pad_length)
|
||||||
}
|
}
|
||||||
|
|
@ -229,7 +246,7 @@ object AvatarConverter {
|
||||||
equip.GUID,
|
equip.GUID,
|
||||||
5,
|
5,
|
||||||
DetailedLockerContainerData(
|
DetailedLockerContainerData(
|
||||||
CommonFieldData(PlanetSideEmpire.NEUTRAL, false, false, true, None, false, None, None, PlanetSideGUID(0)),
|
CommonFieldData(PlanetSideEmpire.NEUTRAL, bops=false, alternate=false, v1=true, None, jammered=false, None, None, PlanetSideGUID(0)),
|
||||||
None
|
None
|
||||||
)
|
)
|
||||||
))
|
))
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
// Copyright (c) 2017 PSForever
|
// Copyright (c) 2017 PSForever
|
||||||
package net.psforever.packet.game.objectcreate
|
package net.psforever.packet.game.objectcreate
|
||||||
|
|
||||||
import net.psforever.objects.avatar.Cosmetic
|
import net.psforever.objects.avatar.BattleRank
|
||||||
import net.psforever.packet.{Marshallable, PacketHelpers}
|
import net.psforever.packet.{Marshallable, PacketHelpers}
|
||||||
|
import net.psforever.types.{Cosmetic, UniformStyle}
|
||||||
import scodec.codecs._
|
import scodec.codecs._
|
||||||
import scodec.{Attempt, Codec, Err}
|
import scodec.{Attempt, Codec, Err}
|
||||||
import shapeless.{::, HNil}
|
import shapeless.{::, HNil}
|
||||||
|
|
@ -26,24 +27,6 @@ object ImplantEffects extends Enumeration {
|
||||||
implicit val codec = PacketHelpers.createEnumerationCodec(this, uint4L)
|
implicit val codec = PacketHelpers.createEnumerationCodec(this, uint4L)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Values for the four different color designs that impact a player's uniform.
|
|
||||||
* Exo-suits get minor graphical updates at the following battle rank levels: seven (1), fourteen (2), and twenty-five (4).
|
|
||||||
* The values 3 and 5 also exist and are visually descriptive to the third upgrade.
|
|
||||||
*/
|
|
||||||
object UniformStyle extends Enumeration {
|
|
||||||
type Type = Value
|
|
||||||
|
|
||||||
val Normal = Value(0)
|
|
||||||
val FirstUpgrade = Value(1)
|
|
||||||
val SecondUpgrade = Value(2)
|
|
||||||
val SecondUpgradeEx = Value(3)
|
|
||||||
val ThirdUpgrade = Value(4)
|
|
||||||
val ThirdUpgradeEx = Value(5)
|
|
||||||
|
|
||||||
implicit val codec = PacketHelpers.createEnumerationCodec(this, uint(3))
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A representation of a portion of an avatar's `ObjectCreateDetailedMessage` packet data.<br>
|
* A representation of a portion of an avatar's `ObjectCreateDetailedMessage` packet data.<br>
|
||||||
* <br>
|
* <br>
|
||||||
|
|
@ -70,9 +53,9 @@ object UniformStyle extends Enumeration {
|
||||||
* cosmetic armor associated with the command rank will be applied automatically
|
* cosmetic armor associated with the command rank will be applied automatically
|
||||||
* @param implant_effects the effects of implants that can be seen on a player's character;
|
* @param implant_effects the effects of implants that can be seen on a player's character;
|
||||||
* the number of entries equates to the number of effects applied;
|
* the number of entries equates to the number of effects applied;
|
||||||
* the maximu number of effects is three
|
* the maximum number of effects is three
|
||||||
* @param cosmetics optional decorative features that are added to the player's head model by console/chat commands;
|
* @param cosmetics optional decorative features that are added to the player's head model by console/chat commands;
|
||||||
* they become available at battle rank 24, but here they require the third uniform upgrade (rank 25);
|
* they become available at battle rank 24;
|
||||||
* these flags do not exist if they are not applicable
|
* these flags do not exist if they are not applicable
|
||||||
* @param is_backpack this player character should be depicted as a corpse;
|
* @param is_backpack this player character should be depicted as a corpse;
|
||||||
* corpses are either coffins (defunct), backpacks (normal), or a pastry (festive);
|
* corpses are either coffins (defunct), backpacks (normal), or a pastry (festive);
|
||||||
|
|
@ -83,7 +66,7 @@ object UniformStyle extends Enumeration {
|
||||||
final case class CharacterData(
|
final case class CharacterData(
|
||||||
health: Int,
|
health: Int,
|
||||||
armor: Int,
|
armor: Int,
|
||||||
uniform_upgrade: UniformStyle.Value,
|
uniform_upgrade: UniformStyle,
|
||||||
unk: Int,
|
unk: Int,
|
||||||
command_rank: Int,
|
command_rank: Int,
|
||||||
implant_effects: List[ImplantEffects.Value],
|
implant_effects: List[ImplantEffects.Value],
|
||||||
|
|
@ -124,7 +107,7 @@ object CharacterData extends Marshallable[CharacterData] {
|
||||||
def apply(
|
def apply(
|
||||||
health: Int,
|
health: Int,
|
||||||
armor: Int,
|
armor: Int,
|
||||||
uniform: UniformStyle.Value,
|
uniform: UniformStyle,
|
||||||
cr: Int,
|
cr: Int,
|
||||||
implant_effects: List[ImplantEffects.Value],
|
implant_effects: List[ImplantEffects.Value],
|
||||||
cosmetics: Option[Set[Cosmetic]]
|
cosmetics: Option[Set[Cosmetic]]
|
||||||
|
|
@ -139,7 +122,7 @@ object CharacterData extends Marshallable[CharacterData] {
|
||||||
uint(3) :: //uniform_upgrade is actually interpreted as a 6u field, but the lower 3u seems to be discarded
|
uint(3) :: //uniform_upgrade is actually interpreted as a 6u field, but the lower 3u seems to be discarded
|
||||||
("command_rank" | uintL(3)) ::
|
("command_rank" | uintL(3)) ::
|
||||||
listOfN(uint2, "implant_effects" | ImplantEffects.codec) ::
|
listOfN(uint2, "implant_effects" | ImplantEffects.codec) ::
|
||||||
conditional(style.id > UniformStyle.SecondUpgrade.id, "cosmetics" | Cosmetic.codec)
|
("cosmetics" | conditional(BattleRank.showCosmetics(style), Cosmetic.codec))
|
||||||
})
|
})
|
||||||
).exmap[CharacterData](
|
).exmap[CharacterData](
|
||||||
{
|
{
|
||||||
|
|
@ -178,7 +161,7 @@ object CharacterData extends Marshallable[CharacterData] {
|
||||||
uint(3) :: //uniform_upgrade is actually interpreted as a 6u field, but the lower 3u seems to be discarded
|
uint(3) :: //uniform_upgrade is actually interpreted as a 6u field, but the lower 3u seems to be discarded
|
||||||
("command_rank" | uintL(3)) ::
|
("command_rank" | uintL(3)) ::
|
||||||
listOfN(uint2, "implant_effects" | ImplantEffects.codec) ::
|
listOfN(uint2, "implant_effects" | ImplantEffects.codec) ::
|
||||||
conditional(style.id > UniformStyle.SecondUpgrade.id, "cosmetics" | Cosmetic.codec)
|
("cosmetics" | conditional(BattleRank.showCosmetics(style), Cosmetic.codec))
|
||||||
}
|
}
|
||||||
).exmap[CharacterData](
|
).exmap[CharacterData](
|
||||||
{
|
{
|
||||||
|
|
@ -200,7 +183,12 @@ object CharacterData extends Marshallable[CharacterData] {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
case CharacterData(_, _, uniform, unk, cr, implant_effects, cosmetics) =>
|
case CharacterData(_, _, uniform, unk, cr, implant_effects, cosmetics) =>
|
||||||
Attempt.Successful(uniform :: unk :: cr :: implant_effects :: cosmetics :: HNil)
|
val cos = if (BattleRank.showCosmetics(uniform)) {
|
||||||
|
cosmetics.orElse(Some(Set[Cosmetic]()))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
Attempt.Successful(uniform :: unk :: cr :: implant_effects :: cos :: HNil)
|
||||||
|
|
||||||
case _ =>
|
case _ =>
|
||||||
Attempt.Failure(Err("invalid character data; can not decode"))
|
Attempt.Failure(Err("invalid character data; can not decode"))
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,9 @@
|
||||||
package net.psforever.packet.game.objectcreate
|
package net.psforever.packet.game.objectcreate
|
||||||
|
|
||||||
import net.psforever.newcodecs.newcodecs
|
import net.psforever.newcodecs.newcodecs
|
||||||
import net.psforever.objects.avatar.{BattleRank, Certification, Cosmetic}
|
import net.psforever.objects.avatar.{BattleRank, Certification}
|
||||||
import net.psforever.packet.{Marshallable, PacketHelpers}
|
import net.psforever.packet.{Marshallable, PacketHelpers}
|
||||||
import net.psforever.types.{ExoSuitType, ImplantType}
|
import net.psforever.types.{Cosmetic, ExoSuitType, ImplantType}
|
||||||
import scodec.{Attempt, Codec}
|
import scodec.{Attempt, Codec}
|
||||||
import scodec.codecs._
|
import scodec.codecs._
|
||||||
import shapeless.{::, HNil}
|
import shapeless.{::, HNil}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
package net.psforever.objects.avatar
|
package net.psforever.types
|
||||||
|
|
||||||
import enumeratum.values.{IntEnum, IntEnumEntry}
|
import enumeratum.values.{IntEnum, IntEnumEntry}
|
||||||
import scodec.{Attempt, Codec}
|
|
||||||
import scodec.codecs.uint
|
import scodec.codecs.uint
|
||||||
|
import scodec.{Attempt, Codec}
|
||||||
|
|
||||||
/** Avatar cosmetic options */
|
/** Avatar cosmetic options */
|
||||||
sealed abstract class Cosmetic(val value: Int) extends IntEnumEntry
|
sealed abstract class Cosmetic(val value: Int) extends IntEnumEntry
|
||||||
|
|
@ -58,9 +58,8 @@ case object Cosmetic extends IntEnum[Cosmetic] {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Codec for object create messages */
|
/** Codec for object create messages */
|
||||||
implicit val codec: Codec[Set[Cosmetic]] = uint(5).exmap(
|
implicit val codec: Codec[Set[Cosmetic]] = uint(bits = 5).exmap(
|
||||||
value => Attempt.Successful(Cosmetic.valuesFromObjectCreateValue(value)),
|
value => Attempt.Successful(Cosmetic.valuesFromObjectCreateValue(value)),
|
||||||
cosmetics => Attempt.Successful(Cosmetic.valuesToObjectCreateValue(cosmetics))
|
cosmetics => Attempt.Successful(Cosmetic.valuesToObjectCreateValue(cosmetics))
|
||||||
)
|
)
|
||||||
|
|
||||||
}
|
}
|
||||||
28
src/main/scala/net/psforever/types/UniformStyle.scala
Normal file
28
src/main/scala/net/psforever/types/UniformStyle.scala
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
// Copyright (c) 2017 PSForever
|
||||||
|
package net.psforever.types
|
||||||
|
|
||||||
|
import enumeratum.values.{IntEnum, IntEnumEntry}
|
||||||
|
import net.psforever.packet.PacketHelpers
|
||||||
|
import scodec.codecs._
|
||||||
|
import scodec.Codec
|
||||||
|
|
||||||
|
sealed abstract class UniformStyle(val value: Int) extends IntEnumEntry
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Values for the four different color designs that impact a player's uniform.
|
||||||
|
* Exo-suits get minor graphical updates at the following battle rank levels: seven (1), fourteen (2), and twenty-five (4).
|
||||||
|
* At battle rank twenty-four (3), the style does not update visually but switches to one suitable for display of cosmetics.
|
||||||
|
* The design for value 5 is visually descriptive of the third upgrade.
|
||||||
|
*/
|
||||||
|
object UniformStyle extends IntEnum[UniformStyle] {
|
||||||
|
val values: IndexedSeq[UniformStyle] = findValues
|
||||||
|
|
||||||
|
case object Normal extends UniformStyle(value = 0)
|
||||||
|
case object FirstUpgrade extends UniformStyle(value = 1)
|
||||||
|
case object SecondUpgrade extends UniformStyle(value = 2)
|
||||||
|
case object SecondUpgradeBR24 extends UniformStyle(value = 3)
|
||||||
|
case object ThirdUpgrade extends UniformStyle(value = 4)
|
||||||
|
case object ThirdUpgradeEx extends UniformStyle(value = 5)
|
||||||
|
|
||||||
|
implicit val codec: Codec[UniformStyle] = PacketHelpers.createIntEnumCodec(this, uint(bits = 3))
|
||||||
|
}
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
// Copyright (c) 2017 PSForever
|
// Copyright (c) 2017 PSForever
|
||||||
package game.objectcreate
|
package game.objectcreate
|
||||||
|
|
||||||
import net.psforever.objects.avatar.Cosmetic
|
|
||||||
import net.psforever.packet.PacketCoding
|
import net.psforever.packet.PacketCoding
|
||||||
import net.psforever.packet.game.ObjectCreateMessage
|
import net.psforever.packet.game.ObjectCreateMessage
|
||||||
import net.psforever.packet.game.objectcreate._
|
import net.psforever.packet.game.objectcreate._
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
// Copyright (c) 2017 PSForever
|
// Copyright (c) 2017 PSForever
|
||||||
package game.objectcreatedetailed
|
package game.objectcreatedetailed
|
||||||
|
|
||||||
import net.psforever.objects.avatar.{BattleRank, Certification, Cosmetic}
|
import net.psforever.objects.avatar.{BattleRank, Certification}
|
||||||
import org.specs2.mutable._
|
import org.specs2.mutable._
|
||||||
import net.psforever.packet._
|
import net.psforever.packet._
|
||||||
import net.psforever.packet.game.ObjectCreateDetailedMessage
|
import net.psforever.packet.game.ObjectCreateDetailedMessage
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
// Copyright (c) 2017 PSForever
|
// Copyright (c) 2017 PSForever
|
||||||
package game.objectcreatevehicle
|
package game.objectcreatevehicle
|
||||||
|
|
||||||
import net.psforever.objects.avatar.Cosmetic
|
|
||||||
import net.psforever.packet._
|
import net.psforever.packet._
|
||||||
import net.psforever.packet.game.objectcreate._
|
import net.psforever.packet.game.objectcreate._
|
||||||
import net.psforever.packet.game.ObjectCreateMessage
|
import net.psforever.packet.game.ObjectCreateMessage
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue