mirror of
https://github.com/psforever/PSF-LoginServer.git
synced 2026-01-19 18:44:45 +00:00
Avatar Persistence
* Add AvatarActor: Responsible for managing the session's avatar object * Convert Avatar object to case class * Add persistence for BEP, CEP, implants, certs and cosmetics * Add cosmetic chat commands and handle UI packet * Add /setbr, /setcr, /certadd, /addbep, /addcep GM commands * Convert zone maps to JSON * Update to Scala 2.13.3 and fix warnings * Fix MAX cooldowns not being applied when purchased manually * Normalize database table names to singular * Add docker image build
This commit is contained in:
parent
1efbedcf8e
commit
3bdc681c9d
23
.github/workflows/ci.yaml
vendored
23
.github/workflows/ci.yaml
vendored
|
|
@ -1,7 +1,7 @@
|
||||||
name: CI
|
name: CI
|
||||||
on: [push, pull_request]
|
on: [push, pull_request]
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
test:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
services:
|
services:
|
||||||
postgres:
|
postgres:
|
||||||
|
|
@ -43,4 +43,23 @@ jobs:
|
||||||
uses: actions/upload-artifact@v2
|
uses: actions/upload-artifact@v2
|
||||||
with:
|
with:
|
||||||
name: pslogin.zip
|
name: pslogin.zip
|
||||||
path: target/pslogin*.zip
|
path: target/pslogin*.zip
|
||||||
|
docker:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
- name: Inject slug/short variables
|
||||||
|
uses: rlespinasse/github-slug-action@v2.x
|
||||||
|
- name: Set variables
|
||||||
|
run: |
|
||||||
|
echo "::set-env name=REPOSITORY::$(echo $GITHUB_REPOSITORY | tr '[A-Z]' '[a-z]')"
|
||||||
|
- name: Build and push Docker image
|
||||||
|
uses: docker/build-push-action@v1.1.0
|
||||||
|
with:
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ github.token }}
|
||||||
|
registry: docker.pkg.github.com
|
||||||
|
repository: ${{ env.REPOSITORY }}/server
|
||||||
|
tag_with_sha: true
|
||||||
|
tag_with_ref: true
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version = 2.6.1
|
version = 2.6.4
|
||||||
preset = defaultWithAlign
|
preset = defaultWithAlign
|
||||||
maxColumn = 120
|
maxColumn = 120
|
||||||
31
.travis.yml
31
.travis.yml
|
|
@ -1,31 +0,0 @@
|
||||||
language: scala
|
|
||||||
jdk: oraclejdk8
|
|
||||||
dist: trusty
|
|
||||||
|
|
||||||
scala:
|
|
||||||
- 2.13.2
|
|
||||||
env:
|
|
||||||
- SBT_COMMAND="test:compile quiet:test packArchiveZip"
|
|
||||||
- SBT_COMMAND="coverage test:compile quiet:test coverageReport"
|
|
||||||
|
|
||||||
cache:
|
|
||||||
directories:
|
|
||||||
- $HOME/.ivy2/cache
|
|
||||||
- $HOME/.sbt
|
|
||||||
before_cache:
|
|
||||||
- rm -fv $HOME/.ivy2/.sbt.ivy.lock
|
|
||||||
- find $HOME/.ivy2/cache -name "ivydata-*.properties" -print -delete
|
|
||||||
- find $HOME/.sbt -name "*.lock" -print -delete
|
|
||||||
|
|
||||||
before_script:
|
|
||||||
- wget https://github.com/psforever/PSCrypto/releases/download/v1.1/pscrypto-lib-1.1.zip
|
|
||||||
- unzip pscrypto-lib-1.1.zip
|
|
||||||
script:
|
|
||||||
- sbt ++$TRAVIS_SCALA_VERSION $SBT_COMMAND
|
|
||||||
after_success:
|
|
||||||
- bash <(curl -s https://codecov.io/bash)
|
|
||||||
|
|
||||||
addons:
|
|
||||||
artifacts:
|
|
||||||
paths:
|
|
||||||
- $(ls target/pslogin*.zip | tr "\n" ":")
|
|
||||||
73
build.sbt
73
build.sbt
|
|
@ -3,7 +3,7 @@ import xerial.sbt.pack.PackPlugin._
|
||||||
lazy val commonSettings = Seq(
|
lazy val commonSettings = Seq(
|
||||||
organization := "net.psforever",
|
organization := "net.psforever",
|
||||||
version := "1.0.2-SNAPSHOT",
|
version := "1.0.2-SNAPSHOT",
|
||||||
scalaVersion := "2.13.2",
|
scalaVersion := "2.13.3",
|
||||||
Global / cancelable := false,
|
Global / cancelable := false,
|
||||||
semanticdbEnabled := true,
|
semanticdbEnabled := true,
|
||||||
semanticdbVersion := scalafixSemanticdb.revision,
|
semanticdbVersion := scalafixSemanticdb.revision,
|
||||||
|
|
@ -43,40 +43,43 @@ lazy val commonSettings = Seq(
|
||||||
classLoaderLayeringStrategy := ClassLoaderLayeringStrategy.Flat,
|
classLoaderLayeringStrategy := ClassLoaderLayeringStrategy.Flat,
|
||||||
resolvers += "Sonatype OSS Snapshots" at "https://oss.sonatype.org/content/repositories/snapshots",
|
resolvers += "Sonatype OSS Snapshots" at "https://oss.sonatype.org/content/repositories/snapshots",
|
||||||
libraryDependencies ++= Seq(
|
libraryDependencies ++= Seq(
|
||||||
"com.typesafe.akka" %% "akka-actor" % "2.6.6",
|
"com.typesafe.akka" %% "akka-actor" % "2.6.6",
|
||||||
"com.typesafe.akka" %% "akka-slf4j" % "2.6.6",
|
"com.typesafe.akka" %% "akka-slf4j" % "2.6.6",
|
||||||
"com.typesafe.akka" %% "akka-protobuf-v3" % "2.6.6",
|
"com.typesafe.akka" %% "akka-protobuf-v3" % "2.6.6",
|
||||||
"com.typesafe.akka" %% "akka-stream" % "2.6.6",
|
"com.typesafe.akka" %% "akka-stream" % "2.6.6",
|
||||||
"com.typesafe.akka" %% "akka-testkit" % "2.6.6" % "test",
|
"com.typesafe.akka" %% "akka-testkit" % "2.6.6" % "test",
|
||||||
"com.typesafe.akka" %% "akka-actor-typed" % "2.6.6",
|
"com.typesafe.akka" %% "akka-actor-typed" % "2.6.6",
|
||||||
"com.typesafe.akka" %% "akka-cluster-typed" % "2.6.6",
|
"com.typesafe.akka" %% "akka-cluster-typed" % "2.6.6",
|
||||||
"com.typesafe.scala-logging" %% "scala-logging" % "3.9.2",
|
"com.typesafe.scala-logging" %% "scala-logging" % "3.9.2",
|
||||||
"org.specs2" %% "specs2-core" % "4.9.4" % "test",
|
"org.specs2" %% "specs2-core" % "4.9.4" % "test",
|
||||||
"org.scalatest" %% "scalatest" % "3.1.2" % "test",
|
"org.scalatest" %% "scalatest" % "3.1.2" % "test",
|
||||||
"org.scodec" %% "scodec-core" % "1.11.7",
|
"org.scodec" %% "scodec-core" % "1.11.7",
|
||||||
"net.java.dev.jna" % "jna" % "5.5.0",
|
"net.java.dev.jna" % "jna" % "5.5.0",
|
||||||
"com.typesafe.akka" %% "akka-slf4j" % "2.6.5",
|
"com.typesafe.akka" %% "akka-slf4j" % "2.6.5",
|
||||||
"ch.qos.logback" % "logback-classic" % "1.2.3",
|
"ch.qos.logback" % "logback-classic" % "1.2.3",
|
||||||
"org.log4s" %% "log4s" % "1.8.2",
|
"org.log4s" %% "log4s" % "1.8.2",
|
||||||
"org.fusesource.jansi" % "jansi" % "1.12",
|
"org.fusesource.jansi" % "jansi" % "1.12",
|
||||||
"org.scoverage" %% "scalac-scoverage-plugin" % "1.4.1",
|
"org.scoverage" %% "scalac-scoverage-plugin" % "1.4.1",
|
||||||
"com.github.nscala-time" %% "nscala-time" % "2.24.0",
|
"com.github.nscala-time" %% "nscala-time" % "2.24.0",
|
||||||
"com.github.t3hnar" %% "scala-bcrypt" % "4.1",
|
"com.github.t3hnar" %% "scala-bcrypt" % "4.1",
|
||||||
"org.scala-graph" %% "graph-core" % "1.13.1",
|
"org.scala-graph" %% "graph-core" % "1.13.1",
|
||||||
"io.kamon" %% "kamon-bundle" % "2.1.0",
|
"io.kamon" %% "kamon-bundle" % "2.1.0",
|
||||||
"io.kamon" %% "kamon-apm-reporter" % "2.1.0",
|
"io.kamon" %% "kamon-apm-reporter" % "2.1.0",
|
||||||
"org.json4s" %% "json4s-native" % "3.6.8",
|
"org.json4s" %% "json4s-native" % "3.6.8",
|
||||||
"com.typesafe.akka" %% "akka-stream" % "2.6.5",
|
"io.getquill" %% "quill-jasync-postgres" % "3.5.2",
|
||||||
"io.getquill" %% "quill-jasync-postgres" % "3.5.2",
|
"org.flywaydb" % "flyway-core" % "6.5.0",
|
||||||
"org.flywaydb" % "flyway-core" % "6.5.0",
|
"org.postgresql" % "postgresql" % "42.2.14",
|
||||||
"org.postgresql" % "postgresql" % "42.2.14",
|
"com.typesafe" % "config" % "1.4.0",
|
||||||
"com.typesafe" % "config" % "1.4.0",
|
"com.github.pureconfig" %% "pureconfig" % "0.13.0",
|
||||||
"com.github.pureconfig" %% "pureconfig" % "0.13.0",
|
"com.beachape" %% "enumeratum" % "1.6.1",
|
||||||
"com.beachape" %% "enumeratum" % "1.6.1",
|
"joda-time" % "joda-time" % "2.10.6",
|
||||||
"joda-time" % "joda-time" % "2.10.6",
|
"commons-io" % "commons-io" % "2.6",
|
||||||
"commons-io" % "commons-io" % "2.6",
|
"com.github.scopt" %% "scopt" % "4.0.0-RC2",
|
||||||
"com.github.scopt" %% "scopt" % "4.0.0-RC2",
|
"io.sentry" % "sentry-logback" % "1.7.30",
|
||||||
"io.sentry" % "sentry-logback" % "1.7.30"
|
"io.circe" %% "circe-core" % "0.12.3",
|
||||||
|
"io.circe" %% "circe-generic" % "0.12.3",
|
||||||
|
"io.circe" %% "circe-parser" % "0.12.3",
|
||||||
|
"org.scala-lang.modules" %% "scala-parallel-collections" % "0.2.0"
|
||||||
),
|
),
|
||||||
// TODO(chord): remove exclusion when SessionActor is refactored: https://github.com/psforever/PSF-LoginServer/issues/279
|
// TODO(chord): remove exclusion when SessionActor is refactored: https://github.com/psforever/PSF-LoginServer/issues/279
|
||||||
coverageExcludedPackages := "net\\.psforever\\.actors\\.session\\.SessionActor.*;net\\.psforever\\.zones\\.zonemaps.*"
|
coverageExcludedPackages := "net\\.psforever\\.actors\\.session\\.SessionActor.*;net\\.psforever\\.zones\\.zonemaps.*"
|
||||||
|
|
|
||||||
1334
common/src/main/resources/zonemaps/lattice.json
Normal file
1334
common/src/main/resources/zonemaps/lattice.json
Normal file
File diff suppressed because it is too large
Load diff
24936
common/src/main/resources/zonemaps/map01.json
Normal file
24936
common/src/main/resources/zonemaps/map01.json
Normal file
File diff suppressed because it is too large
Load diff
30019
common/src/main/resources/zonemaps/map02.json
Normal file
30019
common/src/main/resources/zonemaps/map02.json
Normal file
File diff suppressed because it is too large
Load diff
44488
common/src/main/resources/zonemaps/map03.json
Normal file
44488
common/src/main/resources/zonemaps/map03.json
Normal file
File diff suppressed because it is too large
Load diff
36818
common/src/main/resources/zonemaps/map04.json
Normal file
36818
common/src/main/resources/zonemaps/map04.json
Normal file
File diff suppressed because it is too large
Load diff
25222
common/src/main/resources/zonemaps/map05.json
Normal file
25222
common/src/main/resources/zonemaps/map05.json
Normal file
File diff suppressed because it is too large
Load diff
28966
common/src/main/resources/zonemaps/map06.json
Normal file
28966
common/src/main/resources/zonemaps/map06.json
Normal file
File diff suppressed because it is too large
Load diff
33594
common/src/main/resources/zonemaps/map07.json
Normal file
33594
common/src/main/resources/zonemaps/map07.json
Normal file
File diff suppressed because it is too large
Load diff
25495
common/src/main/resources/zonemaps/map08.json
Normal file
25495
common/src/main/resources/zonemaps/map08.json
Normal file
File diff suppressed because it is too large
Load diff
34777
common/src/main/resources/zonemaps/map09.json
Normal file
34777
common/src/main/resources/zonemaps/map09.json
Normal file
File diff suppressed because it is too large
Load diff
31579
common/src/main/resources/zonemaps/map10.json
Normal file
31579
common/src/main/resources/zonemaps/map10.json
Normal file
File diff suppressed because it is too large
Load diff
14653
common/src/main/resources/zonemaps/map11.json
Normal file
14653
common/src/main/resources/zonemaps/map11.json
Normal file
File diff suppressed because it is too large
Load diff
13574
common/src/main/resources/zonemaps/map12.json
Normal file
13574
common/src/main/resources/zonemaps/map12.json
Normal file
File diff suppressed because it is too large
Load diff
13522
common/src/main/resources/zonemaps/map13.json
Normal file
13522
common/src/main/resources/zonemaps/map13.json
Normal file
File diff suppressed because it is too large
Load diff
9414
common/src/main/resources/zonemaps/map96.json
Normal file
9414
common/src/main/resources/zonemaps/map96.json
Normal file
File diff suppressed because it is too large
Load diff
11078
common/src/main/resources/zonemaps/map97.json
Normal file
11078
common/src/main/resources/zonemaps/map97.json
Normal file
File diff suppressed because it is too large
Load diff
8153
common/src/main/resources/zonemaps/map98.json
Normal file
8153
common/src/main/resources/zonemaps/map98.json
Normal file
File diff suppressed because it is too large
Load diff
3928
common/src/main/resources/zonemaps/map99.json
Normal file
3928
common/src/main/resources/zonemaps/map99.json
Normal file
File diff suppressed because it is too large
Load diff
11910
common/src/main/resources/zonemaps/ugd01.json
Normal file
11910
common/src/main/resources/zonemaps/ugd01.json
Normal file
File diff suppressed because it is too large
Load diff
20295
common/src/main/resources/zonemaps/ugd02.json
Normal file
20295
common/src/main/resources/zonemaps/ugd02.json
Normal file
File diff suppressed because it is too large
Load diff
14549
common/src/main/resources/zonemaps/ugd03.json
Normal file
14549
common/src/main/resources/zonemaps/ugd03.json
Normal file
File diff suppressed because it is too large
Load diff
11572
common/src/main/resources/zonemaps/ugd04.json
Normal file
11572
common/src/main/resources/zonemaps/ugd04.json
Normal file
File diff suppressed because it is too large
Load diff
8218
common/src/main/resources/zonemaps/ugd05.json
Normal file
8218
common/src/main/resources/zonemaps/ugd05.json
Normal file
File diff suppressed because it is too large
Load diff
11676
common/src/main/resources/zonemaps/ugd06.json
Normal file
11676
common/src/main/resources/zonemaps/ugd06.json
Normal file
File diff suppressed because it is too large
Load diff
1329
common/src/main/scala/net/psforever/actors/session/AvatarActor.scala
Normal file
1329
common/src/main/scala/net/psforever/actors/session/AvatarActor.scala
Normal file
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -140,7 +140,7 @@ class BuildingActor(
|
||||||
}
|
}
|
||||||
building.Faction = faction
|
building.Faction = faction
|
||||||
galaxyService ! GalaxyServiceMessage(GalaxyAction.MapUpdate(building.infoUpdateMessage()))
|
galaxyService ! GalaxyServiceMessage(GalaxyAction.MapUpdate(building.infoUpdateMessage()))
|
||||||
zone.LocalEvents ! LocalServiceMessage(zone.Id, LocalAction.SetEmpire(building.GUID, faction))
|
zone.LocalEvents ! LocalServiceMessage(zone.id, LocalAction.SetEmpire(building.GUID, faction))
|
||||||
Behaviors.same
|
Behaviors.same
|
||||||
|
|
||||||
case MapUpdate() =>
|
case MapUpdate() =>
|
||||||
|
|
|
||||||
|
|
@ -9,10 +9,12 @@ import net.psforever.objects.serverobject.structures.StructureType
|
||||||
import net.psforever.objects.zones.Zone
|
import net.psforever.objects.zones.Zone
|
||||||
import net.psforever.objects.{ConstructionItem, PlanetSideGameObject, Player, Vehicle}
|
import net.psforever.objects.{ConstructionItem, PlanetSideGameObject, Player, Vehicle}
|
||||||
import net.psforever.types.{PlanetSideEmpire, PlanetSideGUID, Vector3}
|
import net.psforever.types.{PlanetSideEmpire, PlanetSideGUID, Vector3}
|
||||||
|
|
||||||
import scala.collection.mutable.ListBuffer
|
import scala.collection.mutable.ListBuffer
|
||||||
import akka.actor.typed.scaladsl.adapter._
|
import akka.actor.typed.scaladsl.adapter._
|
||||||
import net.psforever.util.Database._
|
import net.psforever.util.Database._
|
||||||
import net.psforever.persistence
|
import net.psforever.persistence
|
||||||
|
|
||||||
import scala.util.{Failure, Success}
|
import scala.util.{Failure, Success}
|
||||||
import scala.concurrent.ExecutionContext.Implicits.global
|
import scala.concurrent.ExecutionContext.Implicits.global
|
||||||
|
|
||||||
|
|
@ -29,7 +31,11 @@ object ZoneActor {
|
||||||
final case class GetZone(replyTo: ActorRef[ZoneResponse]) extends Command
|
final case class GetZone(replyTo: ActorRef[ZoneResponse]) extends Command
|
||||||
|
|
||||||
final case class ZoneResponse(zone: Zone)
|
final case class ZoneResponse(zone: Zone)
|
||||||
|
/*
|
||||||
|
final case class AddAvatar(avatar: Avatar) extends Command
|
||||||
|
|
||||||
|
final case class RemoveAvatar(avatar: Avatar) extends Command
|
||||||
|
*/
|
||||||
final case class AddPlayer(player: Player) extends Command
|
final case class AddPlayer(player: Player) extends Command
|
||||||
|
|
||||||
final case class RemovePlayer(player: Player) extends Command
|
final case class RemovePlayer(player: Player) extends Command
|
||||||
|
|
|
||||||
|
|
@ -57,7 +57,7 @@ class CryptoSessionActor extends Actor with MDCContextAware {
|
||||||
this.sessionId = sharedSessionId
|
this.sessionId = sharedSessionId
|
||||||
leftRef = sender()
|
leftRef = sender()
|
||||||
if (pipe.hasNext) {
|
if (pipe.hasNext) {
|
||||||
rightRef = pipe.next // who ever we send to has to send something back to us
|
rightRef = pipe.next() // who ever we send to has to send something back to us
|
||||||
rightRef !> HelloFriend(sessionId, pipe)
|
rightRef !> HelloFriend(sessionId, pipe)
|
||||||
} else {
|
} else {
|
||||||
rightRef = sender()
|
rightRef = sender()
|
||||||
|
|
|
||||||
|
|
@ -62,7 +62,7 @@ class LoginSessionActor extends Actor with MDCContextAware {
|
||||||
this.sessionId = aSessionId
|
this.sessionId = aSessionId
|
||||||
leftRef = sender()
|
leftRef = sender()
|
||||||
if (pipe.hasNext) {
|
if (pipe.hasNext) {
|
||||||
rightRef = pipe.next
|
rightRef = pipe.next()
|
||||||
rightRef !> HelloFriend(aSessionId, pipe)
|
rightRef !> HelloFriend(aSessionId, pipe)
|
||||||
} else {
|
} else {
|
||||||
rightRef = sender()
|
rightRef = sender()
|
||||||
|
|
@ -174,7 +174,7 @@ class LoginSessionActor extends Actor with MDCContextAware {
|
||||||
log.info(s"$account")
|
log.info(s"$account")
|
||||||
(account.inactive, password.isBcrypted(account.passhash)) match {
|
(account.inactive, password.isBcrypted(account.passhash)) match {
|
||||||
case (false, true) =>
|
case (false, true) =>
|
||||||
accountIntermediary ! StoreAccountData(newToken, new Account(account.id, account.username, account.gm))
|
accountIntermediary ! StoreAccountData(newToken, Account(account.id, account.username, account.gm))
|
||||||
val future = ctx.run(
|
val future = ctx.run(
|
||||||
query[persistence.Login].insert(
|
query[persistence.Login].insert(
|
||||||
_.accountId -> lift(account.id),
|
_.accountId -> lift(account.id),
|
||||||
|
|
@ -280,7 +280,7 @@ class LoginSessionActor extends Actor with MDCContextAware {
|
||||||
val r = new scala.util.Random
|
val r = new scala.util.Random
|
||||||
val sb = new StringBuilder
|
val sb = new StringBuilder
|
||||||
for (_ <- 1 to 31) {
|
for (_ <- 1 to 31) {
|
||||||
sb.append(r.nextPrintableChar)
|
sb.append(r.nextPrintableChar())
|
||||||
}
|
}
|
||||||
sb.toString
|
sb.toString
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -77,7 +77,7 @@ class PacketCodingActor extends Actor with MDCContextAware {
|
||||||
this.sessionId = sharedSessionId
|
this.sessionId = sharedSessionId
|
||||||
leftRef = sender()
|
leftRef = sender()
|
||||||
if (pipe.hasNext) {
|
if (pipe.hasNext) {
|
||||||
rightRef = pipe.next
|
rightRef = pipe.next()
|
||||||
rightRef !> HelloFriend(sessionId, pipe)
|
rightRef !> HelloFriend(sessionId, pipe)
|
||||||
} else {
|
} else {
|
||||||
rightRef = sender()
|
rightRef = sender()
|
||||||
|
|
@ -127,14 +127,14 @@ class PacketCodingActor extends Actor with MDCContextAware {
|
||||||
relatedALog.clear()
|
relatedALog.clear()
|
||||||
}
|
}
|
||||||
case RawPacket(msg) =>
|
case RawPacket(msg) =>
|
||||||
if (sender == rightRef) { //from LSA, WSA, etc., to network - encode
|
if (sender() == rightRef) { //from LSA, WSA, etc., to network - encode
|
||||||
mtuLimit(msg)
|
mtuLimit(msg)
|
||||||
} else { //from network, to LSA, WSA, etc. - decode
|
} else { //from network, to LSA, WSA, etc. - decode
|
||||||
UnmarshalInnerPacket(msg, "a packet")
|
UnmarshalInnerPacket(msg, "a packet")
|
||||||
}
|
}
|
||||||
//known elevated packet type
|
//known elevated packet type
|
||||||
case ctrl @ ControlPacket(_, packet) =>
|
case ctrl @ ControlPacket(_, packet) =>
|
||||||
if (sender == rightRef) { //from LSA, WSA, to network - encode
|
if (sender() == rightRef) { //from LSA, WSA, to network - encode
|
||||||
PacketCoding.EncodePacket(packet) match {
|
PacketCoding.EncodePacket(packet) match {
|
||||||
case Successful(data) =>
|
case Successful(data) =>
|
||||||
mtuLimit(data.toByteVector)
|
mtuLimit(data.toByteVector)
|
||||||
|
|
@ -148,7 +148,7 @@ class PacketCodingActor extends Actor with MDCContextAware {
|
||||||
}
|
}
|
||||||
//known elevated packet type
|
//known elevated packet type
|
||||||
case game @ GamePacket(_, _, packet) =>
|
case game @ GamePacket(_, _, packet) =>
|
||||||
if (sender == rightRef) { //from LSA, WSA, etc., to network - encode
|
if (sender() == rightRef) { //from LSA, WSA, etc., to network - encode
|
||||||
PacketCoding.EncodePacket(packet) match {
|
PacketCoding.EncodePacket(packet) match {
|
||||||
case Successful(data) =>
|
case Successful(data) =>
|
||||||
mtuLimit(data.toByteVector)
|
mtuLimit(data.toByteVector)
|
||||||
|
|
@ -166,7 +166,7 @@ class PacketCodingActor extends Actor with MDCContextAware {
|
||||||
handleBundlePacket(list)
|
handleBundlePacket(list)
|
||||||
//etc
|
//etc
|
||||||
case msg =>
|
case msg =>
|
||||||
if (sender == rightRef) {
|
if (sender() == rightRef) {
|
||||||
log.trace(s"BASE CASE PACKET SEND, LEFT: $msg")
|
log.trace(s"BASE CASE PACKET SEND, LEFT: $msg")
|
||||||
MDC("sessionId") = sessionId.toString
|
MDC("sessionId") = sessionId.toString
|
||||||
leftRef !> msg
|
leftRef !> msg
|
||||||
|
|
@ -421,7 +421,7 @@ class PacketCodingActor extends Actor with MDCContextAware {
|
||||||
out
|
out
|
||||||
} else {
|
} else {
|
||||||
import net.psforever.packet.{PlanetSideControlPacket, PlanetSideGamePacket}
|
import net.psforever.packet.{PlanetSideControlPacket, PlanetSideGamePacket}
|
||||||
iter.next match {
|
iter.next() match {
|
||||||
case msg: PlanetSideGamePacket =>
|
case msg: PlanetSideGamePacket =>
|
||||||
PacketCoding.EncodePacket(msg) match {
|
PacketCoding.EncodePacket(msg) match {
|
||||||
case Successful(bytecode) =>
|
case Successful(bytecode) =>
|
||||||
|
|
@ -462,7 +462,7 @@ class PacketCodingActor extends Actor with MDCContextAware {
|
||||||
if (!iter.hasNext) {
|
if (!iter.hasNext) {
|
||||||
out
|
out
|
||||||
} else {
|
} else {
|
||||||
val data = iter.next
|
val data = iter.next()
|
||||||
var len = data.length.toInt
|
var len = data.length.toInt
|
||||||
len = len + (if (len < 256) { 1 }
|
len = len + (if (len < 256) { 1 }
|
||||||
else if (len < 65536) { 2 }
|
else if (len < 65536) { 2 }
|
||||||
|
|
|
||||||
|
|
@ -26,8 +26,8 @@ class Session(
|
||||||
) {
|
) {
|
||||||
|
|
||||||
var state: SessionState = New()
|
var state: SessionState = New()
|
||||||
val sessionCreatedTime: DateTime = DateTime.now
|
val sessionCreatedTime: DateTime = DateTime.now()
|
||||||
var sessionEndedTime: DateTime = DateTime.now
|
var sessionEndedTime: DateTime = DateTime.now()
|
||||||
|
|
||||||
val pipeline = sessionPipeline.map { actor =>
|
val pipeline = sessionPipeline.map { actor =>
|
||||||
val a = context.actorOf(actor.props, actor.nameTemplate + sessionId.toString)
|
val a = context.actorOf(actor.props, actor.nameTemplate + sessionId.toString)
|
||||||
|
|
@ -37,7 +37,7 @@ class Session(
|
||||||
|
|
||||||
val pipelineIter = pipeline.iterator
|
val pipelineIter = pipeline.iterator
|
||||||
if (pipelineIter.hasNext) {
|
if (pipelineIter.hasNext) {
|
||||||
pipelineIter.next ! HelloFriend(sessionId, pipelineIter)
|
pipelineIter.next() ! HelloFriend(sessionId, pipelineIter)
|
||||||
}
|
}
|
||||||
|
|
||||||
// statistics
|
// statistics
|
||||||
|
|
@ -74,7 +74,7 @@ class Session(
|
||||||
pipeline.foreach(context.unwatch)
|
pipeline.foreach(context.unwatch)
|
||||||
pipeline.foreach(_ ! PoisonPill)
|
pipeline.foreach(_ ! PoisonPill)
|
||||||
|
|
||||||
sessionEndedTime = DateTime.now
|
sessionEndedTime = DateTime.now()
|
||||||
setState(Closed())
|
setState(Closed())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -54,7 +54,7 @@ class SessionRouter(role: String, pipeline: List[SessionPipeline]) extends Actor
|
||||||
|
|
||||||
override def supervisorStrategy = OneForOneStrategy() { case _ => Stop }
|
override def supervisorStrategy = OneForOneStrategy() { case _ => Stop }
|
||||||
|
|
||||||
override def preStart = {
|
override def preStart() = {
|
||||||
log.info(s"SessionRouter (for ${role}s) initializing ...")
|
log.info(s"SessionRouter (for ${role}s) initializing ...")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,7 @@ class UdpNetworkSimulator(server: ActorRef, params: NetworkSimulatorParameters)
|
||||||
|
|
||||||
// this packet needs to be sent within 20 milliseconds or more
|
// this packet needs to be sent within 20 milliseconds or more
|
||||||
if (lastTime >= 20000000) {
|
if (lastTime >= 20000000) {
|
||||||
server.tell(inPacketQueue.dequeue._1, interface)
|
server.tell(inPacketQueue.dequeue()._1, interface)
|
||||||
} else {
|
} else {
|
||||||
schedule(lastTime.nanoseconds, outbound = false)
|
schedule(lastTime.nanoseconds, outbound = false)
|
||||||
exit = true
|
exit = true
|
||||||
|
|
@ -79,7 +79,7 @@ class UdpNetworkSimulator(server: ActorRef, params: NetworkSimulatorParameters)
|
||||||
|
|
||||||
// this packet needs to be sent within 20 milliseconds or more
|
// this packet needs to be sent within 20 milliseconds or more
|
||||||
if (lastTime >= 20000000) {
|
if (lastTime >= 20000000) {
|
||||||
interface.tell(outPacketQueue.dequeue._1, server)
|
interface.tell(outPacketQueue.dequeue()._1, server)
|
||||||
} else {
|
} else {
|
||||||
schedule(lastTime.nanoseconds, outbound = true)
|
schedule(lastTime.nanoseconds, outbound = true)
|
||||||
exit = true
|
exit = true
|
||||||
|
|
|
||||||
|
|
@ -328,13 +328,13 @@ object WorldSession {
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
localZone.AvatarEvents ! AvatarServiceMessage(
|
localZone.AvatarEvents ! AvatarServiceMessage(
|
||||||
localZone.Id,
|
localZone.id,
|
||||||
AvatarAction.ObjectHeld(localGUID, localPlayer.LastDrawnSlot)
|
AvatarAction.ObjectHeld(localGUID, localPlayer.LastDrawnSlot)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
localPlayer.DrawnSlot = localSlot
|
localPlayer.DrawnSlot = localSlot
|
||||||
localZone.AvatarEvents ! AvatarServiceMessage(
|
localZone.AvatarEvents ! AvatarServiceMessage(
|
||||||
localZone.Id,
|
localZone.id,
|
||||||
AvatarAction.SendResponse(Service.defaultPlayerGUID, ObjectHeldMessage(localGUID, localSlot, false))
|
AvatarAction.SendResponse(Service.defaultPlayerGUID, ObjectHeldMessage(localGUID, localSlot, false))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -380,7 +380,7 @@ object WorldSession {
|
||||||
case Some(_) => ;
|
case Some(_) => ;
|
||||||
case None => //acting on old data?
|
case None => //acting on old data?
|
||||||
localZone.AvatarEvents ! AvatarServiceMessage(
|
localZone.AvatarEvents ! AvatarServiceMessage(
|
||||||
localZone.Id,
|
localZone.id,
|
||||||
AvatarAction.ObjectDelete(Service.defaultPlayerGUID, item_guid)
|
AvatarAction.ObjectDelete(Service.defaultPlayerGUID, item_guid)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ import akka.actor.typed.scaladsl.adapter._
|
||||||
class CmdListPlayers(args: Array[String], services: Map[String, ActorRef]) extends Actor {
|
class CmdListPlayers(args: Array[String], services: Map[String, ActorRef]) extends Actor {
|
||||||
private[this] val log = org.log4s.getLogger(self.path.name)
|
private[this] val log = org.log4s.getLogger(self.path.name)
|
||||||
|
|
||||||
override def preStart = {
|
override def preStart() = {
|
||||||
ServiceManager.receptionist ! Receptionist.Find(
|
ServiceManager.receptionist ! Receptionist.Find(
|
||||||
InterstellarClusterService.InterstellarClusterServiceKey,
|
InterstellarClusterService.InterstellarClusterServiceKey,
|
||||||
context.self
|
context.self
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import akka.actor.{Actor, ActorRef}
|
||||||
import scala.collection.mutable.Map
|
import scala.collection.mutable.Map
|
||||||
|
|
||||||
class CmdShutdown(args: Array[String], services: Map[String, ActorRef]) extends Actor {
|
class CmdShutdown(args: Array[String], services: Map[String, ActorRef]) extends Actor {
|
||||||
override def preStart = {
|
override def preStart() = {
|
||||||
var data = Map[String, Any]()
|
var data = Map[String, Any]()
|
||||||
context.parent ! CommandGoodResponse("Shutting down", data)
|
context.parent ! CommandGoodResponse("Shutting down", data)
|
||||||
context.system.terminate()
|
context.system.terminate()
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,7 @@
|
||||||
// Copyright (c) 2017 PSForever
|
|
||||||
package net.psforever.objects
|
package net.psforever.objects
|
||||||
|
|
||||||
class Account(private val accountId: Int, private val username: String, private val gm: Boolean = false) {
|
case class Account(
|
||||||
def AccountId: Int = accountId
|
id: Int,
|
||||||
def Username: String = username
|
name: String,
|
||||||
def GM: Boolean = gm
|
gm: Boolean = false
|
||||||
}
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,384 +0,0 @@
|
||||||
// Copyright (c) 2017 PSForever
|
|
||||||
package net.psforever.objects
|
|
||||||
|
|
||||||
import net.psforever.objects.avatar.{DeployableToolbox, LoadoutManager}
|
|
||||||
import net.psforever.objects.definition.{AvatarDefinition, ImplantDefinition}
|
|
||||||
import net.psforever.objects.equipment.{EquipmentSize, EquipmentSlot}
|
|
||||||
import net.psforever.packet.game.objectcreate.Cosmetics
|
|
||||||
import net.psforever.types._
|
|
||||||
|
|
||||||
import scala.annotation.tailrec
|
|
||||||
import scala.collection.mutable
|
|
||||||
|
|
||||||
class Avatar(
|
|
||||||
private val char_id: Long,
|
|
||||||
val name: String,
|
|
||||||
val faction: PlanetSideEmpire.Value,
|
|
||||||
val sex: CharacterGender.Value,
|
|
||||||
val head: Int,
|
|
||||||
val voice: CharacterVoice.Value
|
|
||||||
) {
|
|
||||||
|
|
||||||
/** char_id, Character ID; a unique identifier corresponding to a database table row index */
|
|
||||||
/** Battle Experience Points */
|
|
||||||
private var bep: Long = 0
|
|
||||||
|
|
||||||
/** Command Experience Points */
|
|
||||||
private var cep: Long = 0
|
|
||||||
|
|
||||||
/** Cosmetics * */
|
|
||||||
private var pStyle: Option[Cosmetics] = None
|
|
||||||
|
|
||||||
/** Certifications */
|
|
||||||
private val certs: mutable.Set[CertificationType.Value] = mutable.Set[CertificationType.Value]()
|
|
||||||
|
|
||||||
/** Implants<br>
|
|
||||||
* Unlike other objects, all `ImplantSlot` objects are already built into the `Avatar`.
|
|
||||||
* Additionally, implants do not have tightly-coupled "`Definition` objects" that explain a formal implant object.
|
|
||||||
* The `ImplantDefinition` objects themselves are moved around as if they were the implants.
|
|
||||||
* The terms externally used for the states of process is "installed" and "uninstalled."
|
|
||||||
* @see `ImplantSlot`
|
|
||||||
* @see `DetailedCharacterData.implants`
|
|
||||||
*/
|
|
||||||
private val implants: Array[ImplantSlot] = Array.fill[ImplantSlot](3)(new ImplantSlot)
|
|
||||||
|
|
||||||
/** Equipment Loadouts<br>
|
|
||||||
* 0-9 are Infantry loadouts<br>
|
|
||||||
* 10-14 are Vehicle loadouts
|
|
||||||
*/
|
|
||||||
private val equipmentLoadouts: LoadoutManager = new LoadoutManager(15)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Squad Loadouts
|
|
||||||
*/
|
|
||||||
private val squadLoadouts: LoadoutManager = new LoadoutManager(10)
|
|
||||||
|
|
||||||
/** Locker */
|
|
||||||
private val locker: LockerContainer = new LockerContainer() {
|
|
||||||
override def toString: String = {
|
|
||||||
s"$name's ${Definition.Name}"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private val deployables: DeployableToolbox = new DeployableToolbox
|
|
||||||
|
|
||||||
private var firstTimeEvents: List[String] = List[String]()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Looking For Squad:<br>
|
|
||||||
* Indicates both a player state and the text on the marquee under the player nameplate.
|
|
||||||
* Should only be valid when the player is not in a squad.
|
|
||||||
*/
|
|
||||||
private var lfs: Boolean = false
|
|
||||||
|
|
||||||
private var vehicleOwned: Option[PlanetSideGUID] = None
|
|
||||||
|
|
||||||
/** key - object id<br>
|
|
||||||
* value - time last used (ms)
|
|
||||||
*/
|
|
||||||
private var lastUsedEquipmentTimes: mutable.LongMap[Long] = mutable.LongMap[Long]()
|
|
||||||
|
|
||||||
/** exo-suit times are sorted by `Enumeration` order, which was determined by packet process<br>
|
|
||||||
* key - exo-suit id<br>
|
|
||||||
* value - time last used (ms)
|
|
||||||
*/
|
|
||||||
private val lastUsedExoSuitTimes: Array[Long] = Array.fill[Long](ExoSuitType.values.size)(0L)
|
|
||||||
|
|
||||||
/** mechanized exo-suit times are sorted by subtype distinction, which was determined by packet process<br>
|
|
||||||
* key - subtype id<br>
|
|
||||||
* value - time last used (ms)
|
|
||||||
*/
|
|
||||||
private val lastUsedMaxExoSuitTimes: Array[Long] = Array.fill[Long](4)(0L) //invalid, ai, av, aa
|
|
||||||
/** key - object id<br>
|
|
||||||
* value - time last acquired (from a terminal) (ms)
|
|
||||||
*/
|
|
||||||
private var lastPurchaseTimes: mutable.LongMap[Long] = mutable.LongMap[Long]()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* To reload purchase and use timers, a string representing the item must be produced.
|
|
||||||
* Point directly from the object id to the object definition and get the `Name` from that definition.
|
|
||||||
* Allocate only when an item is purchased or used.
|
|
||||||
* The keys match the keys for both `lastUsedEquipmentTimes` and `lastPurchaseTimes`.<br>
|
|
||||||
* key - object id<br>
|
|
||||||
* value - most basic object definition information
|
|
||||||
*/
|
|
||||||
private val objectTypeNameReference: mutable.LongMap[String] = new mutable.LongMap[String]()
|
|
||||||
|
|
||||||
def CharId: Long = char_id
|
|
||||||
|
|
||||||
def BEP: Long = bep
|
|
||||||
|
|
||||||
def BEP_=(battleExperiencePoints: Long): Long = {
|
|
||||||
bep = math.max(0L, math.min(battleExperiencePoints, 4294967295L))
|
|
||||||
BEP
|
|
||||||
}
|
|
||||||
|
|
||||||
def Certifications: mutable.Set[CertificationType.Value] = certs
|
|
||||||
|
|
||||||
def CEP: Long = cep
|
|
||||||
|
|
||||||
def CEP_=(commandExperiencePoints: Long): Long = {
|
|
||||||
cep = math.max(0L, math.min(commandExperiencePoints, 4294967295L))
|
|
||||||
CEP
|
|
||||||
}
|
|
||||||
|
|
||||||
def PersonalStyleFeatures: Option[Cosmetics] = pStyle
|
|
||||||
|
|
||||||
def PersonalStyleFeatures_=(app: Cosmetics): Option[Cosmetics] = {
|
|
||||||
pStyle = Some(app)
|
|
||||||
pStyle
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve the three implant slots for this player.
|
|
||||||
* @return an `Array` of `ImplantSlot` objects
|
|
||||||
*/
|
|
||||||
def Implants: Array[ImplantSlot] = implants
|
|
||||||
|
|
||||||
/**
|
|
||||||
* What kind of implant is installed into the given slot number?
|
|
||||||
* @see `ImplantType`
|
|
||||||
* @param slot the slot number
|
|
||||||
* @return the tye of implant
|
|
||||||
*/
|
|
||||||
def Implant(slot: Int): ImplantType.Value = {
|
|
||||||
if (-1 < slot && slot < implants.length) { implants(slot).Implant }
|
|
||||||
else { ImplantType.None }
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Given a new implant, assign it into a vacant implant slot on this player.<br>
|
|
||||||
* <br>
|
|
||||||
* The implant must be unique in terms of which implants have already been assigned to this player.
|
|
||||||
* Multiple of a type of implant being assigned at once is not supported.
|
|
||||||
* Additionally, the implant is inserted into the earliest yet-unknown but vacant slot.
|
|
||||||
* Implant slots are vacant by just being unlocked or by having their previous implant uninstalled.
|
|
||||||
* @param implant the implant being installed
|
|
||||||
* @return the index of the `ImplantSlot` where the implant was installed
|
|
||||||
*/
|
|
||||||
def InstallImplant(implant: ImplantDefinition): Option[Int] = {
|
|
||||||
implants
|
|
||||||
.find({ p => p.Installed.contains(implant) || p.Implant == implant.Type }) match { //try to find the installed implant
|
|
||||||
case None =>
|
|
||||||
recursiveFindImplantInSlot(implants.iterator, ImplantType.None) match { //install in a free slot
|
|
||||||
case Some(slot) =>
|
|
||||||
implants(slot).Implant = implant
|
|
||||||
Some(slot)
|
|
||||||
case None =>
|
|
||||||
None
|
|
||||||
}
|
|
||||||
case Some(_) =>
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove a specific implant from a player's allocated installed implants.<br>
|
|
||||||
* <br>
|
|
||||||
* Due to the exclusiveness of installed implants,
|
|
||||||
* any implant slot with a matching `Definition` can be uninstalled safely.
|
|
||||||
* (There will never be any doubles.)
|
|
||||||
* This operation can lead to an irregular pattern of installed and uninstalled `ImplantSlot` objects.
|
|
||||||
* Despite that breach of pattern, the logic here is consistent as demonstrated by the client and by packets.
|
|
||||||
* The client also assigns and removes implants based on slot numbers that only express availability of a "slot."
|
|
||||||
* @see `AvatarImplantMessage.implantSlot`
|
|
||||||
* @param implantType the type of implant being uninstalled
|
|
||||||
* @return the index of the `ImplantSlot` where the implant was found and uninstalled
|
|
||||||
*/
|
|
||||||
def UninstallImplant(implantType: ImplantType.Value): Option[Int] = {
|
|
||||||
recursiveFindImplantInSlot(implants.iterator, implantType) match {
|
|
||||||
case Some(slot) =>
|
|
||||||
implants(slot).Implant = None
|
|
||||||
Some(slot)
|
|
||||||
case None =>
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Locate the index of the encountered implant type.
|
|
||||||
* Functional implants may be exclusive in as far as the input `Iterator`'s source is concerned,
|
|
||||||
* but any number of `ImplantType.None` values are alway allowed in the source in any order.
|
|
||||||
* @param iter an `Iterator` of `ImplantSlot` objects
|
|
||||||
* @param implantType the target implant being sought
|
|
||||||
* @param index a defaulted index value representing the structure underlying the `Iterator` param
|
|
||||||
* @return the index where the target implant is installed
|
|
||||||
*/
|
|
||||||
@tailrec private def recursiveFindImplantInSlot(
|
|
||||||
iter: Iterator[ImplantSlot],
|
|
||||||
implantType: ImplantType.Value,
|
|
||||||
index: Int = 0
|
|
||||||
): Option[Int] = {
|
|
||||||
if (!iter.hasNext) {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
val slot = iter.next
|
|
||||||
if (slot.Unlocked && slot.Implant == implantType) {
|
|
||||||
Some(index)
|
|
||||||
} else {
|
|
||||||
recursiveFindImplantInSlot(iter, implantType, index + 1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def ResetAllImplants(): Unit = {
|
|
||||||
implants.foreach(slot => {
|
|
||||||
slot.Installed match {
|
|
||||||
case Some(_) =>
|
|
||||||
slot.Active = false
|
|
||||||
slot.Initialized = false
|
|
||||||
slot.InitializeTime = 0L
|
|
||||||
case None => ;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
def EquipmentLoadouts: LoadoutManager = equipmentLoadouts
|
|
||||||
|
|
||||||
def SquadLoadouts: LoadoutManager = squadLoadouts
|
|
||||||
|
|
||||||
def Locker: LockerContainer = locker
|
|
||||||
|
|
||||||
def FifthSlot: EquipmentSlot = {
|
|
||||||
new OffhandEquipmentSlot(EquipmentSize.Inventory) {
|
|
||||||
val obj = new LockerEquipment(locker)
|
|
||||||
Equipment = obj
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def Deployables: DeployableToolbox = deployables
|
|
||||||
|
|
||||||
def FirstTimeEvents: List[String] = firstTimeEvents
|
|
||||||
|
|
||||||
def FirstTimeEvents_=(event: String): List[String] = FirstTimeEvents_=(List(event))
|
|
||||||
|
|
||||||
def FirstTimeEvents_=(events: List[String]): List[String] = {
|
|
||||||
firstTimeEvents ++= events
|
|
||||||
FirstTimeEvents
|
|
||||||
}
|
|
||||||
|
|
||||||
def LFS: Boolean = lfs
|
|
||||||
|
|
||||||
def LFS_=(looking: Boolean): Boolean = {
|
|
||||||
lfs = looking
|
|
||||||
LFS
|
|
||||||
}
|
|
||||||
|
|
||||||
def VehicleOwned: Option[PlanetSideGUID] = vehicleOwned
|
|
||||||
|
|
||||||
def VehicleOwned_=(guid: PlanetSideGUID): Option[PlanetSideGUID] = VehicleOwned_=(Some(guid))
|
|
||||||
|
|
||||||
def VehicleOwned_=(guid: Option[PlanetSideGUID]): Option[PlanetSideGUID] = {
|
|
||||||
vehicleOwned = guid
|
|
||||||
VehicleOwned
|
|
||||||
}
|
|
||||||
|
|
||||||
def GetLastUsedTime(code: Int): Long = {
|
|
||||||
lastUsedEquipmentTimes.get(code) match {
|
|
||||||
case Some(time) => time
|
|
||||||
case None => 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def GetLastUsedTime(code: ExoSuitType.Value): Long = {
|
|
||||||
lastUsedExoSuitTimes(code.id)
|
|
||||||
}
|
|
||||||
|
|
||||||
def GetLastUsedTime(code: ExoSuitType.Value, subtype: Int): Long = {
|
|
||||||
if (code == ExoSuitType.MAX) {
|
|
||||||
lastUsedMaxExoSuitTimes(subtype)
|
|
||||||
} else {
|
|
||||||
GetLastUsedTime(code)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def GetAllLastUsedTimes: Map[Long, Long] = lastUsedEquipmentTimes.toMap
|
|
||||||
|
|
||||||
def SetLastUsedTime(code: Int, time: Long): Unit = {
|
|
||||||
lastUsedEquipmentTimes += code.toLong -> time
|
|
||||||
}
|
|
||||||
|
|
||||||
def SetLastUsedTime(code: ExoSuitType.Value): Unit = SetLastUsedTime(code, System.currentTimeMillis())
|
|
||||||
|
|
||||||
def SetLastUsedTime(code: ExoSuitType.Value, time: Long): Unit = {
|
|
||||||
lastUsedExoSuitTimes(code.id) = time
|
|
||||||
}
|
|
||||||
|
|
||||||
def SetLastUsedTime(code: ExoSuitType.Value, subtype: Int, time: Long): Unit = {
|
|
||||||
if (code == ExoSuitType.MAX) {
|
|
||||||
lastUsedMaxExoSuitTimes(subtype) = time
|
|
||||||
}
|
|
||||||
SetLastUsedTime(code, time)
|
|
||||||
}
|
|
||||||
|
|
||||||
def GetLastPurchaseTime(code: Int): Long = {
|
|
||||||
lastPurchaseTimes.get(code) match {
|
|
||||||
case Some(time) => time
|
|
||||||
case None => 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def GetAllLastPurchaseTimes: Map[Long, Long] = lastPurchaseTimes.toMap
|
|
||||||
|
|
||||||
def SetLastPurchaseTime(code: Int, time: Long): Unit = {
|
|
||||||
lastPurchaseTimes += code.toLong -> time
|
|
||||||
}
|
|
||||||
|
|
||||||
def ObjectTypeNameReference(id: Long): String = {
|
|
||||||
objectTypeNameReference.get(id) match {
|
|
||||||
case Some(objectName) => objectName
|
|
||||||
case None => ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def ObjectTypeNameReference(id: Long, name: String): String = {
|
|
||||||
objectTypeNameReference(id) = name
|
|
||||||
name
|
|
||||||
}
|
|
||||||
|
|
||||||
def Definition: AvatarDefinition = GlobalDefinitions.avatar
|
|
||||||
|
|
||||||
/*
|
|
||||||
Merit Commendations and Ribbons
|
|
||||||
*/
|
|
||||||
// private var tosRibbon : MeritCommendation.Value = MeritCommendation.None
|
|
||||||
// private var upperRibbon : MeritCommendation.Value = MeritCommendation.None
|
|
||||||
// private var middleRibbon : MeritCommendation.Value = MeritCommendation.None
|
|
||||||
// private var lowerRibbon : MeritCommendation.Value = MeritCommendation.None
|
|
||||||
|
|
||||||
def canEqual(other: Any): Boolean = other.isInstanceOf[Avatar]
|
|
||||||
|
|
||||||
override def equals(other: Any): Boolean =
|
|
||||||
other match {
|
|
||||||
case that: Avatar =>
|
|
||||||
(that canEqual this) &&
|
|
||||||
name == that.name &&
|
|
||||||
faction == that.faction &&
|
|
||||||
sex == that.sex &&
|
|
||||||
head == that.head &&
|
|
||||||
voice == that.voice
|
|
||||||
case _ =>
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
override def hashCode(): Int = {
|
|
||||||
val state = Seq(name, faction, sex, head, voice)
|
|
||||||
state.map(_.hashCode()).foldLeft(0)((a, b) => 31 * a + b)
|
|
||||||
}
|
|
||||||
|
|
||||||
override def toString: String = Avatar.toString(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
object Avatar {
|
|
||||||
def apply(
|
|
||||||
name: String,
|
|
||||||
faction: PlanetSideEmpire.Value,
|
|
||||||
sex: CharacterGender.Value,
|
|
||||||
head: Int,
|
|
||||||
voice: CharacterVoice.Value
|
|
||||||
): Avatar = {
|
|
||||||
new Avatar(0L, name, faction, sex, head, voice)
|
|
||||||
}
|
|
||||||
|
|
||||||
def toString(avatar: Avatar): String = s"${avatar.faction} ${avatar.name}"
|
|
||||||
}
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
// Copyright (c) 2017 PSForever
|
// Copyright (c) 2017 PSForever
|
||||||
package net.psforever.objects
|
package net.psforever.objects
|
||||||
|
|
||||||
|
import net.psforever.objects.avatar.Certification
|
||||||
import net.psforever.objects.ce.DeployedItem
|
import net.psforever.objects.ce.DeployedItem
|
||||||
import net.psforever.objects.definition.{ConstructionFireMode, ConstructionItemDefinition}
|
import net.psforever.objects.definition.{ConstructionFireMode, ConstructionItemDefinition}
|
||||||
import net.psforever.objects.equipment.{Equipment, FireModeSwitch}
|
import net.psforever.objects.equipment.{Equipment, FireModeSwitch}
|
||||||
import net.psforever.types.CertificationType
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A type of `Equipment` that can be wielded and applied to the game world to produce other game objects.<br>
|
* A type of `Equipment` that can be wielded and applied to the game world to produce other game objects.<br>
|
||||||
|
|
@ -55,7 +55,7 @@ class ConstructionItem(private val cItemDef: ConstructionItemDefinition)
|
||||||
FireMode.Deployables(ammoTypeIndex)
|
FireMode.Deployables(ammoTypeIndex)
|
||||||
}
|
}
|
||||||
|
|
||||||
def ModePermissions: Set[CertificationType.Value] = FireMode.Permissions(ammoTypeIndex)
|
def ModePermissions: Set[Certification] = FireMode.Permissions(ammoTypeIndex)
|
||||||
|
|
||||||
def Definition: ConstructionItemDefinition = cItemDef
|
def Definition: ConstructionItemDefinition = cItemDef
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,8 @@ object Default {
|
||||||
//cancellable
|
//cancellable
|
||||||
import akka.actor.Cancellable
|
import akka.actor.Cancellable
|
||||||
protected class InternalCancellable extends Cancellable {
|
protected class InternalCancellable extends Cancellable {
|
||||||
override def cancel: Boolean = true
|
override def cancel(): Boolean = true
|
||||||
|
|
||||||
override def isCancelled: Boolean = true
|
override def isCancelled: Boolean = true
|
||||||
}
|
}
|
||||||
private val cancellable: Cancellable = new InternalCancellable
|
private val cancellable: Cancellable = new InternalCancellable
|
||||||
|
|
@ -28,7 +29,7 @@ object Default {
|
||||||
*/
|
*/
|
||||||
private class DefaultActor extends AkkaActor {
|
private class DefaultActor extends AkkaActor {
|
||||||
def receive: Receive = {
|
def receive: Receive = {
|
||||||
case msg => context.system.deadLetters ! DeadLetter(msg, sender, self)
|
case msg => context.system.deadLetters ! DeadLetter(msg, sender(), self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private var defaultRef: ActorRef = ActorRef.noSender
|
private var defaultRef: ActorRef = ActorRef.noSender
|
||||||
|
|
@ -40,7 +41,7 @@ object Default {
|
||||||
*/
|
*/
|
||||||
def apply(sys: ActorSystem): ActorRef = {
|
def apply(sys: ActorSystem): ActorRef = {
|
||||||
if (defaultRef == ActorRef.noSender) {
|
if (defaultRef == ActorRef.noSender) {
|
||||||
defaultRef = sys.actorOf(Props[DefaultActor], name = s"system-default-actor")
|
defaultRef = sys.actorOf(Props[DefaultActor](), name = s"system-default-actor")
|
||||||
}
|
}
|
||||||
defaultRef
|
defaultRef
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,12 +2,13 @@
|
||||||
package net.psforever.objects
|
package net.psforever.objects
|
||||||
|
|
||||||
import akka.actor.ActorRef
|
import akka.actor.ActorRef
|
||||||
|
import net.psforever.objects.avatar.{Avatar, Certification}
|
||||||
|
|
||||||
import scala.concurrent.duration._
|
import scala.concurrent.duration._
|
||||||
import net.psforever.objects.ce.{Deployable, DeployedItem}
|
import net.psforever.objects.ce.{Deployable, DeployedItem}
|
||||||
import net.psforever.objects.zones.Zone
|
import net.psforever.objects.zones.Zone
|
||||||
import net.psforever.packet.game.{DeployableInfo, DeploymentAction}
|
import net.psforever.packet.game.{DeployableInfo, DeploymentAction}
|
||||||
import net.psforever.types.{CertificationType, PlanetSideGUID}
|
import net.psforever.types.PlanetSideGUID
|
||||||
import services.RemoverActor
|
import services.RemoverActor
|
||||||
import services.local.{LocalAction, LocalServiceMessage}
|
import services.local.{LocalAction, LocalServiceMessage}
|
||||||
|
|
||||||
|
|
@ -100,7 +101,7 @@ object Deployables {
|
||||||
*/
|
*/
|
||||||
def Disown(zone: Zone, avatar: Avatar, replyTo: ActorRef): List[PlanetSideGameObject with Deployable] = {
|
def Disown(zone: Zone, avatar: Avatar, replyTo: ActorRef): List[PlanetSideGameObject with Deployable] = {
|
||||||
val (boomers, deployables) =
|
val (boomers, deployables) =
|
||||||
avatar.Deployables
|
avatar.deployables
|
||||||
.Clear()
|
.Clear()
|
||||||
.map(zone.GUID)
|
.map(zone.GUID)
|
||||||
.collect { case Some(obj) => obj.asInstanceOf[PlanetSideGameObject with Deployable] }
|
.collect { case Some(obj) => obj.asInstanceOf[PlanetSideGameObject with Deployable] }
|
||||||
|
|
@ -128,7 +129,7 @@ object Deployables {
|
||||||
*/
|
*/
|
||||||
def InitializeDeployableQuantities(avatar: Avatar): Boolean = {
|
def InitializeDeployableQuantities(avatar: Avatar): Boolean = {
|
||||||
log.info("Setting up combat engineering ...")
|
log.info("Setting up combat engineering ...")
|
||||||
avatar.Deployables.Initialize(avatar.Certifications.toSet)
|
avatar.deployables.Initialize(avatar.certifications)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -137,7 +138,7 @@ object Deployables {
|
||||||
*/
|
*/
|
||||||
def InitializeDeployableUIElements(avatar: Avatar): List[(Int, Int, Int, Int)] = {
|
def InitializeDeployableUIElements(avatar: Avatar): List[(Int, Int, Int, Int)] = {
|
||||||
log.info("Setting up combat engineering UI ...")
|
log.info("Setting up combat engineering UI ...")
|
||||||
avatar.Deployables.UpdateUI()
|
avatar.deployables.UpdateUI()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -149,11 +150,11 @@ object Deployables {
|
||||||
*/
|
*/
|
||||||
def AddToDeployableQuantities(
|
def AddToDeployableQuantities(
|
||||||
avatar: Avatar,
|
avatar: Avatar,
|
||||||
certification: CertificationType.Value,
|
certification: Certification,
|
||||||
certificationSet: Set[CertificationType.Value]
|
certificationSet: Set[Certification]
|
||||||
): List[(Int, Int, Int, Int)] = {
|
): List[(Int, Int, Int, Int)] = {
|
||||||
avatar.Deployables.AddToDeployableQuantities(certification, certificationSet)
|
avatar.deployables.AddToDeployableQuantities(certification, certificationSet)
|
||||||
avatar.Deployables.UpdateUI(certification)
|
avatar.deployables.UpdateUI(certification)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -165,10 +166,10 @@ object Deployables {
|
||||||
*/
|
*/
|
||||||
def RemoveFromDeployableQuantities(
|
def RemoveFromDeployableQuantities(
|
||||||
avatar: Avatar,
|
avatar: Avatar,
|
||||||
certification: CertificationType.Value,
|
certification: Certification,
|
||||||
certificationSet: Set[CertificationType.Value]
|
certificationSet: Set[Certification]
|
||||||
): List[(Int, Int, Int, Int)] = {
|
): List[(Int, Int, Int, Int)] = {
|
||||||
avatar.Deployables.RemoveFromDeployableQuantities(certification, certificationSet)
|
avatar.deployables.RemoveFromDeployableQuantities(certification, certificationSet)
|
||||||
avatar.Deployables.UpdateUI(certification)
|
avatar.deployables.UpdateUI(certification)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -93,7 +93,7 @@ object ExplosiveDeployableControl {
|
||||||
if (target.Definition.DetonateOnJamming) {
|
if (target.Definition.DetonateOnJamming) {
|
||||||
val zone = target.Zone
|
val zone = target.Zone
|
||||||
zone.Activity ! Zone.HotSpot.Activity(cause.target, cause.projectile.owner, cause.hit_pos)
|
zone.Activity ! Zone.HotSpot.Activity(cause.target, cause.projectile.owner, cause.hit_pos)
|
||||||
zone.LocalEvents ! LocalServiceMessage(zone.Id, LocalAction.Detonate(target.GUID, target))
|
zone.LocalEvents ! LocalServiceMessage(zone.id, LocalAction.Detonate(target.GUID, target))
|
||||||
}
|
}
|
||||||
DestructionAwareness(target, cause)
|
DestructionAwareness(target, cause)
|
||||||
}
|
}
|
||||||
|
|
@ -114,12 +114,12 @@ object ExplosiveDeployableControl {
|
||||||
target.Destroyed = true
|
target.Destroyed = true
|
||||||
Deployables.AnnounceDestroyDeployable(target, Some(if (target.Jammed) 0 seconds else 500 milliseconds))
|
Deployables.AnnounceDestroyDeployable(target, Some(if (target.Jammed) 0 seconds else 500 milliseconds))
|
||||||
zone.AvatarEvents ! AvatarServiceMessage(
|
zone.AvatarEvents ! AvatarServiceMessage(
|
||||||
zone.Id,
|
zone.id,
|
||||||
AvatarAction.Destroy(target.GUID, attribution, Service.defaultPlayerGUID, target.Position)
|
AvatarAction.Destroy(target.GUID, attribution, Service.defaultPlayerGUID, target.Position)
|
||||||
)
|
)
|
||||||
if (target.Health == 0) {
|
if (target.Health == 0) {
|
||||||
zone.LocalEvents ! LocalServiceMessage(
|
zone.LocalEvents ! LocalServiceMessage(
|
||||||
zone.Id,
|
zone.id,
|
||||||
LocalAction.TriggerEffect(Service.defaultPlayerGUID, "detonate_damaged_mine", target.GUID)
|
LocalAction.TriggerEffect(Service.defaultPlayerGUID, "detonate_damaged_mine", target.GUID)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
// Copyright (c) 2017 PSForever
|
// Copyright (c) 2017 PSForever
|
||||||
package net.psforever.objects
|
package net.psforever.objects
|
||||||
|
|
||||||
|
import net.psforever.objects.avatar.Certification
|
||||||
import net.psforever.objects.ballistics.Projectiles
|
import net.psforever.objects.ballistics.Projectiles
|
||||||
import net.psforever.objects.ce.{DeployableCategory, DeployedItem}
|
import net.psforever.objects.ce.{DeployableCategory, DeployedItem}
|
||||||
import net.psforever.objects.definition._
|
import net.psforever.objects.definition._
|
||||||
|
|
@ -22,8 +23,7 @@ import net.psforever.objects.serverobject.turret.{FacilityTurretDefinition, Turr
|
||||||
import net.psforever.objects.vehicles.{DestroyedVehicle, InternalTelepadDefinition, SeatArmorRestriction, UtilityType}
|
import net.psforever.objects.vehicles.{DestroyedVehicle, InternalTelepadDefinition, SeatArmorRestriction, UtilityType}
|
||||||
import net.psforever.objects.vital.damage.{DamageCalculations, DamageModifiers}
|
import net.psforever.objects.vital.damage.{DamageCalculations, DamageModifiers}
|
||||||
import net.psforever.objects.vital.{DamageType, StandardResolutions}
|
import net.psforever.objects.vital.{DamageType, StandardResolutions}
|
||||||
import net.psforever.types.{CertificationType, ExoSuitType, PlanetSideEmpire, Vector3}
|
import net.psforever.types.{ExoSuitType, ImplantType, PlanetSideEmpire, Vector3}
|
||||||
|
|
||||||
import scala.collection.mutable
|
import scala.collection.mutable
|
||||||
import scala.concurrent.duration._
|
import scala.concurrent.duration._
|
||||||
|
|
||||||
|
|
@ -53,47 +53,67 @@ object GlobalDefinitions {
|
||||||
/*
|
/*
|
||||||
Implants
|
Implants
|
||||||
*/
|
*/
|
||||||
val advanced_regen = ImplantDefinition(0)
|
val advanced_regen = new ImplantDefinition(ImplantType.AdvancedRegen) {
|
||||||
|
Name = "advanced_regen"
|
||||||
|
}
|
||||||
advanced_regen.InitializationDuration = 120
|
advanced_regen.InitializationDuration = 120
|
||||||
advanced_regen.StaminaCost = 2
|
advanced_regen.StaminaCost = 2
|
||||||
advanced_regen.CostIntervalDefault = 500
|
advanced_regen.CostIntervalDefault = 500
|
||||||
|
|
||||||
val targeting = ImplantDefinition(1)
|
val targeting = new ImplantDefinition(ImplantType.Targeting) {
|
||||||
|
Name = "targeting"
|
||||||
|
}
|
||||||
targeting.InitializationDuration = 60
|
targeting.InitializationDuration = 60
|
||||||
|
|
||||||
val audio_amplifier = ImplantDefinition(2)
|
val audio_amplifier = new ImplantDefinition(ImplantType.AudioAmplifier) {
|
||||||
|
Name = "audio_amplifier"
|
||||||
|
}
|
||||||
audio_amplifier.InitializationDuration = 60
|
audio_amplifier.InitializationDuration = 60
|
||||||
audio_amplifier.StaminaCost = 1
|
audio_amplifier.StaminaCost = 1
|
||||||
audio_amplifier.CostIntervalDefault = 1000
|
audio_amplifier.CostIntervalDefault = 1000
|
||||||
|
|
||||||
val darklight_vision = ImplantDefinition(3)
|
val darklight_vision = new ImplantDefinition(ImplantType.DarklightVision) {
|
||||||
|
Name = "darklight_vision"
|
||||||
|
}
|
||||||
darklight_vision.InitializationDuration = 60
|
darklight_vision.InitializationDuration = 60
|
||||||
darklight_vision.ActivationStaminaCost = 3
|
darklight_vision.ActivationStaminaCost = 3
|
||||||
darklight_vision.StaminaCost = 1
|
darklight_vision.StaminaCost = 1
|
||||||
darklight_vision.CostIntervalDefault = 500
|
darklight_vision.CostIntervalDefault = 500
|
||||||
|
|
||||||
val melee_booster = ImplantDefinition(4)
|
val melee_booster = new ImplantDefinition(ImplantType.MeleeBooster) {
|
||||||
|
Name = "melee_booster"
|
||||||
|
}
|
||||||
melee_booster.InitializationDuration = 120
|
melee_booster.InitializationDuration = 120
|
||||||
melee_booster.StaminaCost = 10
|
melee_booster.StaminaCost = 10
|
||||||
|
|
||||||
val personal_shield = ImplantDefinition(5)
|
val personal_shield = new ImplantDefinition(ImplantType.PersonalShield) {
|
||||||
|
Name = "personal_shield"
|
||||||
|
}
|
||||||
personal_shield.InitializationDuration = 120
|
personal_shield.InitializationDuration = 120
|
||||||
personal_shield.StaminaCost = 1
|
personal_shield.StaminaCost = 1
|
||||||
personal_shield.CostIntervalDefault = 600
|
personal_shield.CostIntervalDefault = 600
|
||||||
|
|
||||||
val range_magnifier = ImplantDefinition(6)
|
val range_magnifier = new ImplantDefinition(ImplantType.RangeMagnifier) {
|
||||||
|
Name = "range_magnifier"
|
||||||
|
}
|
||||||
range_magnifier.InitializationDuration = 60
|
range_magnifier.InitializationDuration = 60
|
||||||
|
|
||||||
val second_wind = ImplantDefinition(7)
|
val second_wind = new ImplantDefinition(ImplantType.SecondWind) {
|
||||||
|
Name = "second_wind"
|
||||||
|
}
|
||||||
second_wind.InitializationDuration = 180
|
second_wind.InitializationDuration = 180
|
||||||
|
|
||||||
val silent_run = ImplantDefinition(8)
|
val silent_run = new ImplantDefinition(ImplantType.SilentRun) {
|
||||||
|
Name = "silent_run"
|
||||||
|
}
|
||||||
silent_run.InitializationDuration = 90
|
silent_run.InitializationDuration = 90
|
||||||
silent_run.StaminaCost = 1
|
silent_run.StaminaCost = 1
|
||||||
silent_run.CostIntervalDefault = 333
|
silent_run.CostIntervalDefault = 333
|
||||||
silent_run.CostIntervalByExoSuitHashMap(ExoSuitType.Agile) = 1000
|
silent_run.CostIntervalByExoSuitHashMap(ExoSuitType.Agile) = 1000
|
||||||
|
|
||||||
val surge = ImplantDefinition(9)
|
val surge = new ImplantDefinition(ImplantType.Surge) {
|
||||||
|
Name = "surge"
|
||||||
|
}
|
||||||
surge.InitializationDuration = 90
|
surge.InitializationDuration = 90
|
||||||
surge.StaminaCost = 1
|
surge.StaminaCost = 1
|
||||||
surge.CostIntervalDefault = 1000
|
surge.CostIntervalDefault = 1000
|
||||||
|
|
@ -1633,7 +1653,7 @@ object GlobalDefinitions {
|
||||||
|
|
||||||
Reinforced.Name = "med_armor"
|
Reinforced.Name = "med_armor"
|
||||||
Reinforced.Descriptor = "reinforced"
|
Reinforced.Descriptor = "reinforced"
|
||||||
Reinforced.Permissions = List(CertificationType.ReinforcedExoSuit)
|
Reinforced.Permissions = List(Certification.ReinforcedExoSuit)
|
||||||
Reinforced.MaxArmor = 200
|
Reinforced.MaxArmor = 200
|
||||||
Reinforced.InventoryScale = InventoryTile.Tile1209
|
Reinforced.InventoryScale = InventoryTile.Tile1209
|
||||||
Reinforced.InventoryOffset = 6
|
Reinforced.InventoryOffset = 6
|
||||||
|
|
@ -1647,7 +1667,7 @@ object GlobalDefinitions {
|
||||||
Reinforced.ResistanceAggravated = 12
|
Reinforced.ResistanceAggravated = 12
|
||||||
|
|
||||||
Infiltration.Name = "infiltration_suit"
|
Infiltration.Name = "infiltration_suit"
|
||||||
Infiltration.Permissions = List(CertificationType.InfiltrationSuit)
|
Infiltration.Permissions = List(Certification.InfiltrationSuit)
|
||||||
Infiltration.MaxArmor = 0
|
Infiltration.MaxArmor = 0
|
||||||
Infiltration.InventoryScale = InventoryTile.Tile66
|
Infiltration.InventoryScale = InventoryTile.Tile66
|
||||||
Infiltration.InventoryOffset = 6
|
Infiltration.InventoryOffset = 6
|
||||||
|
|
@ -1655,8 +1675,7 @@ object GlobalDefinitions {
|
||||||
Infiltration.Holster(4, EquipmentSize.Melee)
|
Infiltration.Holster(4, EquipmentSize.Melee)
|
||||||
|
|
||||||
def CommonMaxConfig(max: SpecialExoSuitDefinition): Unit = {
|
def CommonMaxConfig(max: SpecialExoSuitDefinition): Unit = {
|
||||||
max.Permissions =
|
max.Permissions = List(Certification.AIMAX, Certification.AVMAX, Certification.AAMAX, Certification.UniMAX)
|
||||||
List(CertificationType.AIMAX, CertificationType.AVMAX, CertificationType.AAMAX, CertificationType.UniMAX)
|
|
||||||
max.MaxArmor = 650
|
max.MaxArmor = 650
|
||||||
max.InventoryScale = InventoryTile.Tile1612
|
max.InventoryScale = InventoryTile.Tile1612
|
||||||
max.InventoryOffset = 6
|
max.InventoryOffset = 6
|
||||||
|
|
@ -4720,35 +4739,33 @@ object GlobalDefinitions {
|
||||||
ace.Name = "ace"
|
ace.Name = "ace"
|
||||||
ace.Size = EquipmentSize.Pistol
|
ace.Size = EquipmentSize.Pistol
|
||||||
ace.Modes += new ConstructionFireMode
|
ace.Modes += new ConstructionFireMode
|
||||||
ace.Modes.head.Item(DeployedItem.boomer -> Set(CertificationType.CombatEngineering))
|
ace.Modes.head.Item(DeployedItem.boomer, Set(Certification.CombatEngineering))
|
||||||
ace.Modes += new ConstructionFireMode
|
ace.Modes += new ConstructionFireMode
|
||||||
ace.Modes(1).Item(DeployedItem.he_mine -> Set(CertificationType.CombatEngineering))
|
ace.Modes(1).Item(DeployedItem.he_mine, Set(Certification.CombatEngineering))
|
||||||
ace.Modes(1).Item(DeployedItem.jammer_mine -> Set(CertificationType.AssaultEngineering))
|
ace.Modes(1).Item(DeployedItem.jammer_mine, Set(Certification.AssaultEngineering))
|
||||||
ace.Modes += new ConstructionFireMode
|
ace.Modes += new ConstructionFireMode
|
||||||
ace.Modes(2).Item(DeployedItem.spitfire_turret -> Set(CertificationType.CombatEngineering))
|
ace.Modes(2).Item(DeployedItem.spitfire_turret, Set(Certification.CombatEngineering))
|
||||||
ace.Modes(2).Item(DeployedItem.spitfire_cloaked -> Set(CertificationType.FortificationEngineering))
|
ace.Modes(2).Item(DeployedItem.spitfire_cloaked, Set(Certification.FortificationEngineering))
|
||||||
ace.Modes(2).Item(DeployedItem.spitfire_aa -> Set(CertificationType.FortificationEngineering))
|
ace.Modes(2).Item(DeployedItem.spitfire_aa, Set(Certification.FortificationEngineering))
|
||||||
ace.Modes += new ConstructionFireMode
|
ace.Modes += new ConstructionFireMode
|
||||||
ace.Modes(3).Item(DeployedItem.motionalarmsensor -> Set(CertificationType.CombatEngineering))
|
ace.Modes(3).Item(DeployedItem.motionalarmsensor, Set(Certification.CombatEngineering))
|
||||||
ace
|
ace.Modes(3).Item(DeployedItem.sensor_shield, Set(Certification.AdvancedHacking, Certification.CombatEngineering))
|
||||||
.Modes(3)
|
|
||||||
.Item(DeployedItem.sensor_shield -> Set(CertificationType.AdvancedHacking, CertificationType.CombatEngineering))
|
|
||||||
ace.Tile = InventoryTile.Tile33
|
ace.Tile = InventoryTile.Tile33
|
||||||
|
|
||||||
advanced_ace.Name = "advanced_ace"
|
advanced_ace.Name = "advanced_ace"
|
||||||
advanced_ace.Size = EquipmentSize.Rifle
|
advanced_ace.Size = EquipmentSize.Rifle
|
||||||
advanced_ace.Modes += new ConstructionFireMode
|
advanced_ace.Modes += new ConstructionFireMode
|
||||||
advanced_ace.Modes.head.Item(DeployedItem.tank_traps -> Set(CertificationType.FortificationEngineering))
|
advanced_ace.Modes.head.Item(DeployedItem.tank_traps, Set(Certification.FortificationEngineering))
|
||||||
advanced_ace.Modes += new ConstructionFireMode
|
advanced_ace.Modes += new ConstructionFireMode
|
||||||
advanced_ace.Modes(1).Item(DeployedItem.portable_manned_turret -> Set(CertificationType.AssaultEngineering))
|
advanced_ace.Modes(1).Item(DeployedItem.portable_manned_turret, Set(Certification.AssaultEngineering))
|
||||||
advanced_ace.Modes += new ConstructionFireMode
|
advanced_ace.Modes += new ConstructionFireMode
|
||||||
advanced_ace.Modes(2).Item(DeployedItem.deployable_shield_generator -> Set(CertificationType.AssaultEngineering))
|
advanced_ace.Modes(2).Item(DeployedItem.deployable_shield_generator, Set(Certification.AssaultEngineering))
|
||||||
advanced_ace.Tile = InventoryTile.Tile93
|
advanced_ace.Tile = InventoryTile.Tile93
|
||||||
|
|
||||||
router_telepad.Name = "router_telepad"
|
router_telepad.Name = "router_telepad"
|
||||||
router_telepad.Size = EquipmentSize.Pistol
|
router_telepad.Size = EquipmentSize.Pistol
|
||||||
router_telepad.Modes += new ConstructionFireMode
|
router_telepad.Modes += new ConstructionFireMode
|
||||||
router_telepad.Modes.head.Item(DeployedItem.router_telepad_deployable -> Set(CertificationType.GroundSupport))
|
router_telepad.Modes.head.Item(DeployedItem.router_telepad_deployable, Set(Certification.GroundSupport))
|
||||||
router_telepad.Tile = InventoryTile.Tile33
|
router_telepad.Tile = InventoryTile.Tile33
|
||||||
router_telepad.Packet = new TelepadConverter
|
router_telepad.Packet = new TelepadConverter
|
||||||
|
|
||||||
|
|
@ -6125,7 +6142,8 @@ object GlobalDefinitions {
|
||||||
vulture.TrunkLocation = Vector3(-0.76f, -1.88f, 0f)
|
vulture.TrunkLocation = Vector3(-0.76f, -1.88f, 0f)
|
||||||
vulture.AutoPilotSpeeds = (0, 4)
|
vulture.AutoPilotSpeeds = (0, 4)
|
||||||
vulture.Packet = variantConverter
|
vulture.Packet = variantConverter
|
||||||
vulture.DestroyedModel = Some(DestroyedVehicle.Liberator) //add_property vulture destroyedphysics liberator_destroyed
|
vulture.DestroyedModel =
|
||||||
|
Some(DestroyedVehicle.Liberator) //add_property vulture destroyedphysics liberator_destroyed
|
||||||
vulture.Subtract.Damage1 = 5
|
vulture.Subtract.Damage1 = 5
|
||||||
vulture.JackingDuration = Array(0, 30, 10, 5)
|
vulture.JackingDuration = Array(0, 30, 10, 5)
|
||||||
vulture.DamageUsing = DamageCalculations.AgainstAircraft
|
vulture.DamageUsing = DamageCalculations.AgainstAircraft
|
||||||
|
|
@ -6599,7 +6617,9 @@ object GlobalDefinitions {
|
||||||
vanu_equipment_term.Repairable = false
|
vanu_equipment_term.Repairable = false
|
||||||
|
|
||||||
cert_terminal.Name = "cert_terminal"
|
cert_terminal.Name = "cert_terminal"
|
||||||
cert_terminal.Tab += 0 -> OrderTerminalDefinition.CertificationPage(CertTerminalDefinition.certs)
|
val certs = Certification.values.filter(_.cost != 0)
|
||||||
|
val page = OrderTerminalDefinition.CertificationPage(certs)
|
||||||
|
cert_terminal.Tab += 0 -> page
|
||||||
cert_terminal.MaxHealth = 500
|
cert_terminal.MaxHealth = 500
|
||||||
cert_terminal.Damageable = true
|
cert_terminal.Damageable = true
|
||||||
cert_terminal.Repairable = true
|
cert_terminal.Repairable = true
|
||||||
|
|
@ -6954,4 +6974,5 @@ object GlobalDefinitions {
|
||||||
generator.RepairIfDestroyed = true
|
generator.RepairIfDestroyed = true
|
||||||
generator.Subtract.Damage1 = 9
|
generator.Subtract.Damage1 = 9
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,133 +0,0 @@
|
||||||
// Copyright (c) 2017 PSForever
|
|
||||||
package net.psforever.objects
|
|
||||||
|
|
||||||
import net.psforever.objects.definition.ImplantDefinition
|
|
||||||
import net.psforever.types.{ExoSuitType, ImplantType}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A slot "on the player" into which an implant is installed.
|
|
||||||
* In total, players have three implant slots.<br>
|
|
||||||
* <br>
|
|
||||||
* All implants slots start as "locked" and must be "unlocked" through battle rank advancement.
|
|
||||||
* Only after it is "unlocked" may an implant be "installed" into the slot.
|
|
||||||
* Upon installation, it undergoes an initialization period and then, after which, it is ready for user activation.
|
|
||||||
* Being jammed de-activates the implant, put it into a state of "not being ready," and causes the initialization to repeat.
|
|
||||||
*/
|
|
||||||
class ImplantSlot {
|
|
||||||
|
|
||||||
/** is this slot available for holding an implant */
|
|
||||||
private var unlocked: Boolean = false
|
|
||||||
|
|
||||||
/** whether this implant is ready for use */
|
|
||||||
private var initialized: Boolean = false
|
|
||||||
|
|
||||||
/**
|
|
||||||
*/
|
|
||||||
private var initializeTime: Long = 0L
|
|
||||||
|
|
||||||
/** is this implant active */
|
|
||||||
private var active: Boolean = false
|
|
||||||
|
|
||||||
/** what implant is currently installed in this slot; None if there is no implant currently installed */
|
|
||||||
private var implant: Option[ImplantDefinition] = None
|
|
||||||
|
|
||||||
def InitializeTime: Long = initializeTime
|
|
||||||
|
|
||||||
def InitializeTime_=(time: Long): Long = {
|
|
||||||
initializeTime = time
|
|
||||||
InitializeTime
|
|
||||||
}
|
|
||||||
|
|
||||||
def Unlocked: Boolean = unlocked
|
|
||||||
|
|
||||||
def Unlocked_=(lock: Boolean): Boolean = {
|
|
||||||
unlocked = lock || unlocked //do not let re-lock
|
|
||||||
Unlocked
|
|
||||||
}
|
|
||||||
|
|
||||||
def Initialized: Boolean = initialized
|
|
||||||
|
|
||||||
def Initialized_=(init: Boolean): Boolean = {
|
|
||||||
initialized = Installed.isDefined && init
|
|
||||||
Active = Active && initialized //can not be active just yet
|
|
||||||
Initialized
|
|
||||||
}
|
|
||||||
|
|
||||||
def Active: Boolean = active
|
|
||||||
|
|
||||||
def Active_=(state: Boolean): Boolean = {
|
|
||||||
active = Initialized && state
|
|
||||||
Active
|
|
||||||
}
|
|
||||||
|
|
||||||
def Implant: ImplantType.Value =
|
|
||||||
Installed match {
|
|
||||||
case Some(idef) =>
|
|
||||||
idef.Type
|
|
||||||
case None =>
|
|
||||||
Active = false
|
|
||||||
Initialized = false
|
|
||||||
ImplantType.None
|
|
||||||
}
|
|
||||||
|
|
||||||
def Implant_=(anImplant: ImplantDefinition): ImplantType.Value = {
|
|
||||||
Implant_=(Some(anImplant))
|
|
||||||
}
|
|
||||||
|
|
||||||
def Implant_=(anImplant: Option[ImplantDefinition]): ImplantType.Value = {
|
|
||||||
if (Unlocked) {
|
|
||||||
anImplant match {
|
|
||||||
case Some(_) =>
|
|
||||||
implant = anImplant
|
|
||||||
case None =>
|
|
||||||
implant = None
|
|
||||||
}
|
|
||||||
Active = false
|
|
||||||
Initialized = false
|
|
||||||
}
|
|
||||||
Implant
|
|
||||||
}
|
|
||||||
|
|
||||||
def Installed: Option[ImplantDefinition] = implant
|
|
||||||
|
|
||||||
def MaxTimer: Long =
|
|
||||||
Implant match {
|
|
||||||
case ImplantType.None =>
|
|
||||||
-1L
|
|
||||||
case _ =>
|
|
||||||
Installed.get.InitializationDuration
|
|
||||||
}
|
|
||||||
|
|
||||||
def ActivationCharge: Int = {
|
|
||||||
if (Active) {
|
|
||||||
Installed.get.ActivationStaminaCost
|
|
||||||
} else {
|
|
||||||
0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculate the stamina consumption of the implant for any given moment of being active after its activation.
|
|
||||||
* @param suit the exo-suit being worn
|
|
||||||
* @return the amount of stamina (energy) that is consumed
|
|
||||||
*/
|
|
||||||
def Charge(suit: ExoSuitType.Value): Int = {
|
|
||||||
if (Active) {
|
|
||||||
val inst = Installed.get
|
|
||||||
inst.StaminaCost
|
|
||||||
} else {
|
|
||||||
0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def Jammed(): Unit = {
|
|
||||||
Active = false
|
|
||||||
Initialized = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
object ImplantSlot {
|
|
||||||
def apply(): ImplantSlot = {
|
|
||||||
new ImplantSlot()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
// Copyright (c) 2017 PSForever
|
// Copyright (c) 2017 PSForever
|
||||||
package net.psforever.objects
|
package net.psforever.objects
|
||||||
|
|
||||||
|
import net.psforever.objects.avatar.Avatar
|
||||||
|
|
||||||
import scala.collection.concurrent.{Map, TrieMap}
|
import scala.collection.concurrent.{Map, TrieMap}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -25,13 +27,21 @@ private class LivePlayerList {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def Update(sessionId: Long, avatar: Avatar): Unit = {
|
||||||
|
sessionMap.get(sessionId) match {
|
||||||
|
case Some(_) =>
|
||||||
|
sessionMap(sessionId) = avatar
|
||||||
|
case None => ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
def Remove(sessionId: Long): Option[Avatar] = {
|
def Remove(sessionId: Long): Option[Avatar] = {
|
||||||
sessionMap.remove(sessionId)
|
sessionMap.remove(sessionId)
|
||||||
}
|
}
|
||||||
|
|
||||||
def Shutdown: List[Avatar] = {
|
def Shutdown: List[Avatar] = {
|
||||||
val list = sessionMap.values.toList
|
val list = sessionMap.values.toList
|
||||||
sessionMap.clear
|
sessionMap.clear()
|
||||||
list
|
list
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -68,15 +78,19 @@ object LivePlayerList {
|
||||||
/**
|
/**
|
||||||
* Create a mapped entry between the user's session and a user's character.
|
* Create a mapped entry between the user's session and a user's character.
|
||||||
* Neither the player nor the session may exist in the current mappings if this is to work.
|
* Neither the player nor the session may exist in the current mappings if this is to work.
|
||||||
|
*
|
||||||
* @param sessionId the session
|
* @param sessionId the session
|
||||||
* @param avatar the character
|
* @param avatar the character
|
||||||
* @return `true`, if the session was association was made; `false`, otherwise
|
* @return `true`, if the session was association was made; `false`, otherwise
|
||||||
*/
|
*/
|
||||||
def Add(sessionId: Long, avatar: Avatar): Boolean = Instance.Add(sessionId, avatar)
|
def Add(sessionId: Long, avatar: Avatar): Boolean = Instance.Add(sessionId, avatar)
|
||||||
|
|
||||||
|
def Update(sessionId: Long, avatar: Avatar): Unit = Instance.Update(sessionId, avatar)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove all entries related to the given session identifier from the mappings.
|
* Remove all entries related to the given session identifier from the mappings.
|
||||||
* The character no longer counts as "online."
|
* The character no longer counts as "online."
|
||||||
|
*
|
||||||
* @param sessionId the session
|
* @param sessionId the session
|
||||||
* @return any character that was afffected by the mapping removal
|
* @return any character that was afffected by the mapping removal
|
||||||
*/
|
*/
|
||||||
|
|
@ -84,6 +98,7 @@ object LivePlayerList {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hastily remove all mappings and ids.
|
* Hastily remove all mappings and ids.
|
||||||
|
*
|
||||||
* @return an unsorted list of the characters that were still online
|
* @return an unsorted list of the characters that were still online
|
||||||
*/
|
*/
|
||||||
def Shutdown: List[Avatar] = Instance.Shutdown
|
def Shutdown: List[Avatar] = Instance.Shutdown
|
||||||
|
|
|
||||||
|
|
@ -70,14 +70,14 @@ trait NtuStorageBehavior extends Actor {
|
||||||
def NtuStorageObject: NtuContainer = null
|
def NtuStorageObject: NtuContainer = null
|
||||||
|
|
||||||
def storageBehavior: Receive = {
|
def storageBehavior: Receive = {
|
||||||
case Ntu.Offer(src) => HandleNtuOffer(sender, src)
|
case Ntu.Offer(src) => HandleNtuOffer(sender(), src)
|
||||||
|
|
||||||
case Ntu.Grant(_, 0) | Ntu.Request(0, 0) | TransferBehavior.Stopping() => StopNtuBehavior(sender)
|
case Ntu.Grant(_, 0) | Ntu.Request(0, 0) | TransferBehavior.Stopping() => StopNtuBehavior(sender())
|
||||||
|
|
||||||
case Ntu.Request(min, max) => HandleNtuRequest(sender, min, max)
|
case Ntu.Request(min, max) => HandleNtuRequest(sender(), min, max)
|
||||||
|
|
||||||
case Ntu.Grant(src, amount) => HandleNtuGrant(sender, src, amount)
|
case Ntu.Grant(src, amount) => HandleNtuGrant(sender(), src, amount)
|
||||||
case NtuCommand.Grant(src, amount) => HandleNtuGrant(sender, src, amount)
|
case NtuCommand.Grant(src, amount) => HandleNtuGrant(sender(), src, amount)
|
||||||
}
|
}
|
||||||
|
|
||||||
def HandleNtuOffer(sender: ActorRef, src: NtuContainer): Unit
|
def HandleNtuOffer(sender: ActorRef, src: NtuContainer): Unit
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,8 @@
|
||||||
// Copyright (c) 2017 PSForever
|
// Copyright (c) 2017 PSForever
|
||||||
package net.psforever.objects
|
package net.psforever.objects
|
||||||
|
|
||||||
import net.psforever.objects.avatar.LoadoutManager
|
import net.psforever.objects.avatar.{Avatar, LoadoutManager}
|
||||||
import net.psforever.objects.definition.{
|
import net.psforever.objects.definition.{AvatarDefinition, ExoSuitDefinition, SpecialExoSuitDefinition}
|
||||||
AvatarDefinition,
|
|
||||||
ExoSuitDefinition,
|
|
||||||
ImplantDefinition,
|
|
||||||
SpecialExoSuitDefinition
|
|
||||||
}
|
|
||||||
import net.psforever.objects.equipment.{Equipment, EquipmentSize, EquipmentSlot, JammableUnit}
|
import net.psforever.objects.equipment.{Equipment, EquipmentSize, EquipmentSlot, JammableUnit}
|
||||||
import net.psforever.objects.inventory.{Container, GridInventory, InventoryItem}
|
import net.psforever.objects.inventory.{Container, GridInventory, InventoryItem}
|
||||||
import net.psforever.objects.serverobject.PlanetSideServerObject
|
import net.psforever.objects.serverobject.PlanetSideServerObject
|
||||||
|
|
@ -15,13 +10,12 @@ import net.psforever.objects.serverobject.affinity.FactionAffinity
|
||||||
import net.psforever.objects.vital.resistance.ResistanceProfile
|
import net.psforever.objects.vital.resistance.ResistanceProfile
|
||||||
import net.psforever.objects.vital.{DamageResistanceModel, Vitality}
|
import net.psforever.objects.vital.{DamageResistanceModel, Vitality}
|
||||||
import net.psforever.objects.zones.ZoneAware
|
import net.psforever.objects.zones.ZoneAware
|
||||||
import net.psforever.packet.game.objectcreate.{Cosmetics, DetailedCharacterData, PersonalStyle}
|
|
||||||
import net.psforever.types.{PlanetSideGUID, _}
|
import net.psforever.types.{PlanetSideGUID, _}
|
||||||
|
|
||||||
import scala.annotation.tailrec
|
import scala.annotation.tailrec
|
||||||
import scala.util.{Success, Try}
|
import scala.util.{Success, Try}
|
||||||
|
|
||||||
class Player(private val core: Avatar)
|
class Player(var avatar: Avatar)
|
||||||
extends PlanetSideServerObject
|
extends PlanetSideServerObject
|
||||||
with FactionAffinity
|
with FactionAffinity
|
||||||
with Vitality
|
with Vitality
|
||||||
|
|
@ -32,7 +26,6 @@ class Player(private val core: Avatar)
|
||||||
Health = 0 //player health is artificially managed as a part of their lifecycle; start entity as dead
|
Health = 0 //player health is artificially managed as a part of their lifecycle; start entity as dead
|
||||||
Destroyed = true //see isAlive
|
Destroyed = true //see isAlive
|
||||||
private var backpack: Boolean = false
|
private var backpack: Boolean = false
|
||||||
private var stamina: Int = 0
|
|
||||||
private var armor: Int = 0
|
private var armor: Int = 0
|
||||||
|
|
||||||
private var capacitor: Float = 0f
|
private var capacitor: Float = 0f
|
||||||
|
|
@ -40,8 +33,6 @@ class Player(private val core: Avatar)
|
||||||
private var capacitorLastUsedMillis: Long = 0
|
private var capacitorLastUsedMillis: Long = 0
|
||||||
private var capacitorLastChargedMillis: Long = 0
|
private var capacitorLastChargedMillis: Long = 0
|
||||||
|
|
||||||
private var maxStamina: Int = 100 //does anything affect this?
|
|
||||||
|
|
||||||
private var exosuit: ExoSuitDefinition = GlobalDefinitions.Standard
|
private var exosuit: ExoSuitDefinition = GlobalDefinitions.Standard
|
||||||
private val freeHand: EquipmentSlot = new OffhandEquipmentSlot(EquipmentSize.Inventory)
|
private val freeHand: EquipmentSlot = new OffhandEquipmentSlot(EquipmentSize.Inventory)
|
||||||
private val holsters: Array[EquipmentSlot] = Array.fill[EquipmentSlot](5)(new EquipmentSlot)
|
private val holsters: Array[EquipmentSlot] = Array.fill[EquipmentSlot](5)(new EquipmentSlot)
|
||||||
|
|
@ -54,9 +45,7 @@ class Player(private val core: Avatar)
|
||||||
private var crouching: Boolean = false
|
private var crouching: Boolean = false
|
||||||
private var jumping: Boolean = false
|
private var jumping: Boolean = false
|
||||||
private var cloaked: Boolean = false
|
private var cloaked: Boolean = false
|
||||||
private var fatigued: Boolean =
|
private var afk: Boolean = false
|
||||||
false // If stamina drops to 0, player is fatigued until regenerating at least 20 stamina
|
|
||||||
private var afk: Boolean = false
|
|
||||||
|
|
||||||
private var vehicleSeated: Option[PlanetSideGUID] = None
|
private var vehicleSeated: Option[PlanetSideGUID] = None
|
||||||
|
|
||||||
|
|
@ -70,36 +59,35 @@ class Player(private val core: Avatar)
|
||||||
|
|
||||||
/** From PlanetsideAttributeMessage */
|
/** From PlanetsideAttributeMessage */
|
||||||
var PlanetsideAttribute: Array[Long] = Array.ofDim(120)
|
var PlanetsideAttribute: Array[Long] = Array.ofDim(120)
|
||||||
var skipStaminaRegenForTurns: Int = 0
|
|
||||||
|
val squadLoadouts = new LoadoutManager(10)
|
||||||
|
|
||||||
Player.SuitSetup(this, exosuit)
|
Player.SuitSetup(this, exosuit)
|
||||||
|
|
||||||
def CharId: Long = core.CharId
|
def Definition: AvatarDefinition = avatar.definition
|
||||||
|
|
||||||
def Name: String = core.name
|
def CharId: Long = avatar.id
|
||||||
|
|
||||||
def Faction: PlanetSideEmpire.Value = core.faction
|
def Name: String = avatar.name
|
||||||
|
|
||||||
def Sex: CharacterGender.Value = core.sex
|
def Faction: PlanetSideEmpire.Value = avatar.faction
|
||||||
|
|
||||||
def Head: Int = core.head
|
def Sex: CharacterGender.Value = avatar.sex
|
||||||
|
|
||||||
def Voice: CharacterVoice.Value = core.voice
|
def Head: Int = avatar.head
|
||||||
|
|
||||||
def LFS: Boolean = core.LFS
|
def Voice: CharacterVoice.Value = avatar.voice
|
||||||
|
|
||||||
def isAlive: Boolean = !Destroyed
|
def isAlive: Boolean = !Destroyed
|
||||||
|
|
||||||
def isBackpack: Boolean = backpack
|
def isBackpack: Boolean = backpack
|
||||||
|
|
||||||
def Spawn: Boolean = {
|
def Spawn(): Boolean = {
|
||||||
if (!isAlive && !isBackpack) {
|
if (!isAlive && !isBackpack) {
|
||||||
Destroyed = false
|
Destroyed = false
|
||||||
Health = Definition.DefaultHealth
|
Health = Definition.DefaultHealth
|
||||||
Stamina = MaxStamina
|
|
||||||
Armor = MaxArmor
|
Armor = MaxArmor
|
||||||
Capacitor = 0
|
Capacitor = 0
|
||||||
ResetAllImplants()
|
|
||||||
}
|
}
|
||||||
isAlive
|
isAlive
|
||||||
}
|
}
|
||||||
|
|
@ -107,7 +95,6 @@ class Player(private val core: Avatar)
|
||||||
def Die: Boolean = {
|
def Die: Boolean = {
|
||||||
Destroyed = true
|
Destroyed = true
|
||||||
Health = 0
|
Health = 0
|
||||||
Stamina = 0
|
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -126,21 +113,6 @@ class Player(private val core: Avatar)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def Stamina: Int = stamina
|
|
||||||
|
|
||||||
def Stamina_=(assignStamina: Int): Int = {
|
|
||||||
stamina = if (isAlive) { math.min(math.max(0, assignStamina), MaxStamina) }
|
|
||||||
else { 0 }
|
|
||||||
Stamina
|
|
||||||
}
|
|
||||||
|
|
||||||
def MaxStamina: Int = maxStamina
|
|
||||||
|
|
||||||
def MaxStamina_=(max: Int): Int = {
|
|
||||||
maxStamina = math.min(math.max(0, max), 65535)
|
|
||||||
MaxStamina
|
|
||||||
}
|
|
||||||
|
|
||||||
def Armor: Int = armor
|
def Armor: Int = armor
|
||||||
|
|
||||||
def Armor_=(assignArmor: Int): Int = {
|
def Armor_=(assignArmor: Int): Int = {
|
||||||
|
|
@ -199,7 +171,7 @@ class Player(private val core: Avatar)
|
||||||
} else if (slot > -1 && slot < 5) {
|
} else if (slot > -1 && slot < 5) {
|
||||||
holsters(slot)
|
holsters(slot)
|
||||||
} else if (slot == 5) {
|
} else if (slot == 5) {
|
||||||
core.FifthSlot
|
avatar.fifthSlot()
|
||||||
} else if (slot == Player.FreeHandSlot) {
|
} else if (slot == Player.FreeHandSlot) {
|
||||||
freeHand
|
freeHand
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -211,10 +183,6 @@ class Player(private val core: Avatar)
|
||||||
|
|
||||||
def Inventory: GridInventory = inventory
|
def Inventory: GridInventory = inventory
|
||||||
|
|
||||||
def Locker: LockerContainer = core.Locker
|
|
||||||
|
|
||||||
def FifthSlot: EquipmentSlot = core.FifthSlot
|
|
||||||
|
|
||||||
override def Fit(obj: Equipment): Option[Int] = {
|
override def Fit(obj: Equipment): Option[Int] = {
|
||||||
recursiveHolsterFit(holsters.iterator, obj.Size) match {
|
recursiveHolsterFit(holsters.iterator, obj.Size) match {
|
||||||
case Some(index) =>
|
case Some(index) =>
|
||||||
|
|
@ -238,7 +206,7 @@ class Player(private val core: Avatar)
|
||||||
if (!iter.hasNext) {
|
if (!iter.hasNext) {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
val slot = iter.next
|
val slot = iter.next()
|
||||||
if (slot.Equipment.isEmpty && slot.Size.equals(objSize)) {
|
if (slot.Equipment.isEmpty && slot.Size.equals(objSize)) {
|
||||||
Some(index)
|
Some(index)
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -278,7 +246,7 @@ class Player(private val core: Avatar)
|
||||||
if (!iter.hasNext) {
|
if (!iter.hasNext) {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
val slot = iter.next
|
val slot = iter.next()
|
||||||
if (slot.Equipment.isDefined && slot.Equipment.get.GUID == guid) {
|
if (slot.Equipment.isDefined && slot.Equipment.get.GUID == guid) {
|
||||||
Some(index)
|
Some(index)
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -343,40 +311,6 @@ class Player(private val core: Avatar)
|
||||||
|
|
||||||
def RadiationShielding = exosuit.RadiationShielding
|
def RadiationShielding = exosuit.RadiationShielding
|
||||||
|
|
||||||
def EquipmentLoadouts: LoadoutManager = core.EquipmentLoadouts
|
|
||||||
|
|
||||||
def SquadLoadouts: LoadoutManager = core.SquadLoadouts
|
|
||||||
|
|
||||||
def BEP: Long = core.BEP
|
|
||||||
|
|
||||||
def CEP: Long = core.CEP
|
|
||||||
|
|
||||||
def Certifications: Set[CertificationType.Value] = core.Certifications.toSet
|
|
||||||
|
|
||||||
/**
|
|
||||||
* What kind of implant is installed into the given slot number?
|
|
||||||
* @see `ImplantType`
|
|
||||||
* @param slot the slot number
|
|
||||||
* @return the tye of implant
|
|
||||||
*/
|
|
||||||
def Implant(slot: Int): ImplantType.Value = core.Implant(slot)
|
|
||||||
|
|
||||||
def ImplantSlot(slot: Int): ImplantSlot = core.Implants(slot)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A read-only `Array` of tuples representing important information about all unlocked implant slots.
|
|
||||||
* @return a maximum of three implant types, initialization times, and active flags
|
|
||||||
*/
|
|
||||||
def Implants: Array[(ImplantType.Value, Long, Boolean)] = {
|
|
||||||
core.Implants.takeWhile(_.Unlocked).map(implant => { (implant.Implant, implant.MaxTimer, implant.Active) })
|
|
||||||
}
|
|
||||||
|
|
||||||
def InstallImplant(implant: ImplantDefinition): Option[Int] = core.InstallImplant(implant)
|
|
||||||
|
|
||||||
def UninstallImplant(implant: ImplantType.Value): Option[Int] = core.UninstallImplant(implant)
|
|
||||||
|
|
||||||
def ResetAllImplants(): Unit = core.ResetAllImplants()
|
|
||||||
|
|
||||||
def FacingYawUpper: Float = facingYawUpper
|
def FacingYawUpper: Float = facingYawUpper
|
||||||
|
|
||||||
def FacingYawUpper_=(facing: Float): Float = {
|
def FacingYawUpper_=(facing: Float): Float = {
|
||||||
|
|
@ -405,13 +339,6 @@ class Player(private val core: Avatar)
|
||||||
Cloaked
|
Cloaked
|
||||||
}
|
}
|
||||||
|
|
||||||
def Fatigued: Boolean = fatigued
|
|
||||||
|
|
||||||
def Fatigued_=(isFatigued: Boolean): Boolean = {
|
|
||||||
fatigued = isFatigued
|
|
||||||
Fatigued
|
|
||||||
}
|
|
||||||
|
|
||||||
def AwayFromKeyboard: Boolean = afk
|
def AwayFromKeyboard: Boolean = afk
|
||||||
|
|
||||||
def AwayFromKeyboard_=(away: Boolean): Boolean = {
|
def AwayFromKeyboard_=(away: Boolean): Boolean = {
|
||||||
|
|
@ -419,66 +346,6 @@ class Player(private val core: Avatar)
|
||||||
AwayFromKeyboard
|
AwayFromKeyboard
|
||||||
}
|
}
|
||||||
|
|
||||||
def PersonalStyleFeatures: Option[Cosmetics] = core.PersonalStyleFeatures
|
|
||||||
|
|
||||||
def AddToPersonalStyle(value: PersonalStyle.Value): (Option[Cosmetics], Option[Cosmetics]) = {
|
|
||||||
val original = core.PersonalStyleFeatures
|
|
||||||
if (DetailedCharacterData.isBR24(core.BEP)) {
|
|
||||||
core.PersonalStyleFeatures = original match {
|
|
||||||
case Some(cosmetic) =>
|
|
||||||
cosmetic + value
|
|
||||||
case None =>
|
|
||||||
Cosmetics(value)
|
|
||||||
}
|
|
||||||
(original, core.PersonalStyleFeatures)
|
|
||||||
} else {
|
|
||||||
(None, None)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def RemoveFromPersonalStyle(value: PersonalStyle.Value): (Option[Cosmetics], Option[Cosmetics]) = {
|
|
||||||
val original = core.PersonalStyleFeatures
|
|
||||||
original match {
|
|
||||||
case Some(cosmetics) =>
|
|
||||||
(original, core.PersonalStyleFeatures = cosmetics - value)
|
|
||||||
case None =>
|
|
||||||
(None, None)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private def BasicFeatureToggle(feature: PersonalStyle.Value): (Option[Cosmetics], Option[Cosmetics]) =
|
|
||||||
core.PersonalStyleFeatures match {
|
|
||||||
case Some(c: Cosmetics) =>
|
|
||||||
if (c.Styles.contains(feature)) {
|
|
||||||
RemoveFromPersonalStyle(feature)
|
|
||||||
} else {
|
|
||||||
AddToPersonalStyle(feature)
|
|
||||||
}
|
|
||||||
case None =>
|
|
||||||
AddToPersonalStyle(feature)
|
|
||||||
}
|
|
||||||
|
|
||||||
def ToggleHelmet: (Option[Cosmetics], Option[Cosmetics]) = BasicFeatureToggle(PersonalStyle.NoHelmet)
|
|
||||||
|
|
||||||
def ToggleShades: (Option[Cosmetics], Option[Cosmetics]) = BasicFeatureToggle(PersonalStyle.Sunglasses)
|
|
||||||
|
|
||||||
def ToggleEarpiece: (Option[Cosmetics], Option[Cosmetics]) = BasicFeatureToggle(PersonalStyle.Earpiece)
|
|
||||||
|
|
||||||
def ToggleHat: (Option[Cosmetics], Option[Cosmetics]) = {
|
|
||||||
core.PersonalStyleFeatures match {
|
|
||||||
case Some(c: Cosmetics) =>
|
|
||||||
if (c.Styles.contains(PersonalStyle.BrimmedCap)) {
|
|
||||||
(RemoveFromPersonalStyle(PersonalStyle.BrimmedCap)._1, AddToPersonalStyle(PersonalStyle.Beret)._2)
|
|
||||||
} else if (c.Styles.contains(PersonalStyle.Beret)) {
|
|
||||||
RemoveFromPersonalStyle(PersonalStyle.Beret)
|
|
||||||
} else {
|
|
||||||
AddToPersonalStyle(PersonalStyle.BrimmedCap)
|
|
||||||
}
|
|
||||||
case None =>
|
|
||||||
AddToPersonalStyle(PersonalStyle.BrimmedCap)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private var usingSpecial: SpecialExoSuitDefinition.Mode.Value => SpecialExoSuitDefinition.Mode.Value =
|
private var usingSpecial: SpecialExoSuitDefinition.Mode.Value => SpecialExoSuitDefinition.Mode.Value =
|
||||||
DefaultUsingSpecial
|
DefaultUsingSpecial
|
||||||
|
|
||||||
|
|
@ -602,8 +469,6 @@ class Player(private val core: Avatar)
|
||||||
isBackpack && (backpackAccess.isEmpty || backpackAccess.contains(player.GUID))
|
isBackpack && (backpackAccess.isEmpty || backpackAccess.contains(player.GUID))
|
||||||
}
|
}
|
||||||
|
|
||||||
def FirstTimeEvents: List[String] = core.FirstTimeEvents
|
|
||||||
|
|
||||||
def VehicleSeated: Option[PlanetSideGUID] = vehicleSeated
|
def VehicleSeated: Option[PlanetSideGUID] = vehicleSeated
|
||||||
|
|
||||||
def VehicleSeated_=(guid: PlanetSideGUID): Option[PlanetSideGUID] = VehicleSeated_=(Some(guid))
|
def VehicleSeated_=(guid: PlanetSideGUID): Option[PlanetSideGUID] = VehicleSeated_=(Some(guid))
|
||||||
|
|
@ -613,57 +478,31 @@ class Player(private val core: Avatar)
|
||||||
VehicleSeated
|
VehicleSeated
|
||||||
}
|
}
|
||||||
|
|
||||||
def VehicleOwned: Option[PlanetSideGUID] = core.VehicleOwned
|
|
||||||
|
|
||||||
def VehicleOwned_=(guid: PlanetSideGUID): Option[PlanetSideGUID] = core.VehicleOwned_=(Some(guid))
|
|
||||||
|
|
||||||
def VehicleOwned_=(guid: Option[PlanetSideGUID]): Option[PlanetSideGUID] = core.VehicleOwned_=(guid)
|
|
||||||
|
|
||||||
def GetLastUsedTime(code: Int): Long = core.GetLastUsedTime(code)
|
|
||||||
|
|
||||||
def GetLastUsedTime(code: ExoSuitType.Value): Long = core.GetLastUsedTime(code)
|
|
||||||
|
|
||||||
def GetLastUsedTime(code: ExoSuitType.Value, subtype: Int): Long = core.GetLastUsedTime(code, subtype)
|
|
||||||
|
|
||||||
def SetLastUsedTime(code: Int, time: Long): Unit = core.SetLastUsedTime(code, time)
|
|
||||||
|
|
||||||
def SetLastUsedTime(code: ExoSuitType.Value): Unit = core.SetLastUsedTime(code)
|
|
||||||
|
|
||||||
def SetLastUsedTime(code: ExoSuitType.Value, time: Long): Unit = core.SetLastUsedTime(code, time)
|
|
||||||
|
|
||||||
def SetLastUsedTime(code: ExoSuitType.Value, subtype: Int): Unit = core.SetLastUsedTime(code, subtype)
|
|
||||||
|
|
||||||
def SetLastUsedTime(code: ExoSuitType.Value, subtype: Int, time: Long): Unit =
|
|
||||||
core.SetLastUsedTime(code, subtype, time)
|
|
||||||
|
|
||||||
def GetLastPurchaseTime(code: Int): Long = core.GetLastPurchaseTime(code)
|
|
||||||
|
|
||||||
def SetLastPurchaseTime(code: Int, time: Long): Unit = core.SetLastPurchaseTime(code, time)
|
|
||||||
|
|
||||||
def ObjectTypeNameReference(id: Long): String = core.ObjectTypeNameReference(id)
|
|
||||||
|
|
||||||
def ObjectTypeNameReference(id: Long, name: String): String = core.ObjectTypeNameReference(id, name)
|
|
||||||
|
|
||||||
def DamageModel = exosuit.asInstanceOf[DamageResistanceModel]
|
def DamageModel = exosuit.asInstanceOf[DamageResistanceModel]
|
||||||
|
|
||||||
def Definition: AvatarDefinition = core.Definition
|
|
||||||
|
|
||||||
def canEqual(other: Any): Boolean = other.isInstanceOf[Player]
|
def canEqual(other: Any): Boolean = other.isInstanceOf[Player]
|
||||||
|
|
||||||
override def equals(other: Any): Boolean =
|
override def equals(other: Any): Boolean =
|
||||||
other match {
|
other match {
|
||||||
case that: Player =>
|
case that: Player =>
|
||||||
(that canEqual this) &&
|
(that canEqual this) &&
|
||||||
core == that.core
|
avatar == that.avatar
|
||||||
case _ =>
|
case _ =>
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
override def hashCode(): Int = {
|
override def hashCode(): Int = {
|
||||||
core.hashCode()
|
avatar.hashCode()
|
||||||
}
|
}
|
||||||
|
|
||||||
override def toString: String = Player.toString(this)
|
override def toString: String = {
|
||||||
|
val guid = if (HasGUID) {
|
||||||
|
s" ${Continent}-${GUID.guid}"
|
||||||
|
} else {
|
||||||
|
""
|
||||||
|
}
|
||||||
|
s"${avatar.name}$guid ${Health}/${MaxHealth} ${Armor}/${MaxArmor}"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
object Player {
|
object Player {
|
||||||
|
|
@ -672,16 +511,6 @@ object Player {
|
||||||
final val HandsDownSlot: Int = 255
|
final val HandsDownSlot: Int = 255
|
||||||
|
|
||||||
final case class Die()
|
final case class Die()
|
||||||
final case class ImplantActivation(slot: Int, status: Int)
|
|
||||||
final case class ImplantInitializationStart(slot: Int)
|
|
||||||
final case class UninitializeImplant(slot: Int)
|
|
||||||
final case class ImplantInitializationComplete(slot: Int)
|
|
||||||
final case class StaminaRegen()
|
|
||||||
final case class StaminaChanged(currentStamina: Option[Int] = None)
|
|
||||||
|
|
||||||
object StaminaChanged {
|
|
||||||
def apply(amount: Int): StaminaChanged = StaminaChanged(Some(amount))
|
|
||||||
}
|
|
||||||
|
|
||||||
def apply(core: Avatar): Player = {
|
def apply(core: Avatar): Player = {
|
||||||
new Player(core)
|
new Player(core)
|
||||||
|
|
@ -698,33 +527,11 @@ object Player {
|
||||||
|
|
||||||
def Respawn(player: Player): Player = {
|
def Respawn(player: Player): Player = {
|
||||||
if (player.Release) {
|
if (player.Release) {
|
||||||
val obj = new Player(player.core)
|
val obj = new Player(player.avatar)
|
||||||
obj.Continent = player.Continent
|
obj.Continent = player.Continent
|
||||||
obj
|
obj
|
||||||
} else {
|
} else {
|
||||||
player
|
player
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def GetHackLevel(player: Player): Int = {
|
|
||||||
if (
|
|
||||||
player.Certifications.contains(CertificationType.ExpertHacking) || player.Certifications.contains(
|
|
||||||
CertificationType.ElectronicsExpert
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
3
|
|
||||||
} else if (player.Certifications.contains(CertificationType.AdvancedHacking)) {
|
|
||||||
2
|
|
||||||
} else if (player.Certifications.contains(CertificationType.Hacking)) {
|
|
||||||
1
|
|
||||||
} else {
|
|
||||||
0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def toString(obj: Player): String = {
|
|
||||||
val guid = if (obj.HasGUID) { s" ${obj.Continent}-${obj.GUID.guid}" }
|
|
||||||
else { "" }
|
|
||||||
s"${obj.core}$guid ${obj.Health}/${obj.MaxHealth} ${obj.Armor}/${obj.MaxArmor}"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -82,7 +82,7 @@ object Players {
|
||||||
if (!iter.hasNext) {
|
if (!iter.hasNext) {
|
||||||
list
|
list
|
||||||
} else {
|
} else {
|
||||||
val slot = iter.next
|
val slot = iter.next()
|
||||||
slot.Equipment match {
|
slot.Equipment match {
|
||||||
case Some(equipment) =>
|
case Some(equipment) =>
|
||||||
slot.Equipment = None
|
slot.Equipment = None
|
||||||
|
|
@ -105,7 +105,7 @@ object Players {
|
||||||
if (!iter.hasNext) {
|
if (!iter.hasNext) {
|
||||||
list
|
list
|
||||||
} else {
|
} else {
|
||||||
val slot = iter.next
|
val slot = iter.next()
|
||||||
if (slot.Equipment.isEmpty) {
|
if (slot.Equipment.isEmpty) {
|
||||||
list.find(item => item.obj.Size == slot.Size) match {
|
list.find(item => item.obj.Size == slot.Size) match {
|
||||||
case Some(obj) =>
|
case Some(obj) =>
|
||||||
|
|
@ -126,11 +126,11 @@ object Players {
|
||||||
case Nil =>
|
case Nil =>
|
||||||
true
|
true
|
||||||
case permissions if subtype != 0 =>
|
case permissions if subtype != 0 =>
|
||||||
val certs = player.Certifications
|
val certs = player.avatar.certifications
|
||||||
certs.intersect(permissions.toSet).nonEmpty &&
|
certs.intersect(permissions.toSet).nonEmpty &&
|
||||||
certs.intersect(InfantryLoadout.DetermineSubtypeC(subtype)).nonEmpty
|
certs.intersect(InfantryLoadout.DetermineSubtypeC(subtype)).nonEmpty
|
||||||
case permissions =>
|
case permissions =>
|
||||||
player.Certifications.intersect(permissions.toSet).nonEmpty
|
player.avatar.certifications.intersect(permissions.toSet).nonEmpty
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -71,7 +71,7 @@ class SensorDeployableControl(sensor: SensorDeployable)
|
||||||
target match {
|
target match {
|
||||||
case obj: PlanetSideServerObject if !jammedSound =>
|
case obj: PlanetSideServerObject if !jammedSound =>
|
||||||
obj.Zone.VehicleEvents ! VehicleServiceMessage(
|
obj.Zone.VehicleEvents ! VehicleServiceMessage(
|
||||||
obj.Zone.Id,
|
obj.Zone.id,
|
||||||
VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, obj.GUID, 54, 1)
|
VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, obj.GUID, 54, 1)
|
||||||
)
|
)
|
||||||
super.StartJammeredSound(obj, dur)
|
super.StartJammeredSound(obj, dur)
|
||||||
|
|
@ -83,7 +83,7 @@ class SensorDeployableControl(sensor: SensorDeployable)
|
||||||
case obj: PlanetSideServerObject with JammableUnit if !obj.Jammed =>
|
case obj: PlanetSideServerObject with JammableUnit if !obj.Jammed =>
|
||||||
val zone = obj.Zone
|
val zone = obj.Zone
|
||||||
zone.LocalEvents ! LocalServiceMessage(
|
zone.LocalEvents ! LocalServiceMessage(
|
||||||
zone.Id,
|
zone.id,
|
||||||
LocalAction.TriggerEffectInfo(Service.defaultPlayerGUID, "on", obj.GUID, false, 1000)
|
LocalAction.TriggerEffectInfo(Service.defaultPlayerGUID, "on", obj.GUID, false, 1000)
|
||||||
)
|
)
|
||||||
super.StartJammeredStatus(obj, dur)
|
super.StartJammeredStatus(obj, dur)
|
||||||
|
|
@ -95,7 +95,7 @@ class SensorDeployableControl(sensor: SensorDeployable)
|
||||||
case obj: PlanetSideServerObject if jammedSound =>
|
case obj: PlanetSideServerObject if jammedSound =>
|
||||||
val zone = obj.Zone
|
val zone = obj.Zone
|
||||||
zone.VehicleEvents ! VehicleServiceMessage(
|
zone.VehicleEvents ! VehicleServiceMessage(
|
||||||
zone.Id,
|
zone.id,
|
||||||
VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, obj.GUID, 54, 0)
|
VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, obj.GUID, 54, 0)
|
||||||
)
|
)
|
||||||
case _ => ;
|
case _ => ;
|
||||||
|
|
@ -107,7 +107,7 @@ class SensorDeployableControl(sensor: SensorDeployable)
|
||||||
target match {
|
target match {
|
||||||
case obj: PlanetSideServerObject with JammableUnit if obj.Jammed =>
|
case obj: PlanetSideServerObject with JammableUnit if obj.Jammed =>
|
||||||
sensor.Zone.LocalEvents ! LocalServiceMessage(
|
sensor.Zone.LocalEvents ! LocalServiceMessage(
|
||||||
sensor.Zone.Id,
|
sensor.Zone.id,
|
||||||
LocalAction.TriggerEffectInfo(Service.defaultPlayerGUID, "on", obj.GUID, true, 1000)
|
LocalAction.TriggerEffectInfo(Service.defaultPlayerGUID, "on", obj.GUID, true, 1000)
|
||||||
)
|
)
|
||||||
case _ => ;
|
case _ => ;
|
||||||
|
|
@ -127,7 +127,7 @@ object SensorDeployableControl {
|
||||||
Deployables.AnnounceDestroyDeployable(target, Some(1 seconds))
|
Deployables.AnnounceDestroyDeployable(target, Some(1 seconds))
|
||||||
val zone = target.Zone
|
val zone = target.Zone
|
||||||
zone.LocalEvents ! LocalServiceMessage(
|
zone.LocalEvents ! LocalServiceMessage(
|
||||||
zone.Id,
|
zone.id,
|
||||||
LocalAction.TriggerEffectInfo(Service.defaultPlayerGUID, "on", target.GUID, false, 1000)
|
LocalAction.TriggerEffectInfo(Service.defaultPlayerGUID, "on", target.GUID, false, 1000)
|
||||||
)
|
)
|
||||||
//position the explosion effect near the bulky area of the sensor stalk
|
//position the explosion effect near the bulky area of the sensor stalk
|
||||||
|
|
@ -143,7 +143,7 @@ object SensorDeployableControl {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
zone.LocalEvents ! LocalServiceMessage(
|
zone.LocalEvents ! LocalServiceMessage(
|
||||||
zone.Id,
|
zone.id,
|
||||||
LocalAction.TriggerEffectLocation(Service.defaultPlayerGUID, "motion_sensor_destroyed", explosionPos, ang)
|
LocalAction.TriggerEffectLocation(Service.defaultPlayerGUID, "motion_sensor_destroyed", explosionPos, ang)
|
||||||
)
|
)
|
||||||
//TODO replaced by an alternate model (charred stub)?
|
//TODO replaced by an alternate model (charred stub)?
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
package net.psforever.objects
|
package net.psforever.objects
|
||||||
|
|
||||||
|
import net.psforever.objects.avatar.Avatar
|
||||||
import net.psforever.objects.zones.{Zone, Zoning}
|
import net.psforever.objects.zones.{Zone, Zoning}
|
||||||
import net.psforever.packet.game.DeadState
|
import net.psforever.packet.game.DeadState
|
||||||
|
|
||||||
|
|
@ -9,7 +10,6 @@ case class Session(
|
||||||
account: Account = null,
|
account: Account = null,
|
||||||
player: Player = null,
|
player: Player = null,
|
||||||
avatar: Avatar = null,
|
avatar: Avatar = null,
|
||||||
admin: Boolean = false,
|
|
||||||
zoningType: Zoning.Method.Value = Zoning.Method.None,
|
zoningType: Zoning.Method.Value = Zoning.Method.None,
|
||||||
deadState: DeadState.Value = DeadState.Alive,
|
deadState: DeadState.Value = DeadState.Alive,
|
||||||
speed: Float = 1.0f,
|
speed: Float = 1.0f,
|
||||||
|
|
|
||||||
|
|
@ -119,7 +119,7 @@ class ShieldGeneratorControl(gen: ShieldGeneratorDeployable)
|
||||||
target match {
|
target match {
|
||||||
case obj: PlanetSideServerObject with JammableUnit if !obj.Jammed =>
|
case obj: PlanetSideServerObject with JammableUnit if !obj.Jammed =>
|
||||||
obj.Zone.VehicleEvents ! VehicleServiceMessage(
|
obj.Zone.VehicleEvents ! VehicleServiceMessage(
|
||||||
obj.Zone.Id,
|
obj.Zone.id,
|
||||||
VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, obj.GUID, 27, 1)
|
VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, obj.GUID, 27, 1)
|
||||||
)
|
)
|
||||||
super.StartJammeredStatus(obj, dur)
|
super.StartJammeredStatus(obj, dur)
|
||||||
|
|
@ -132,7 +132,7 @@ class ShieldGeneratorControl(gen: ShieldGeneratorDeployable)
|
||||||
target match {
|
target match {
|
||||||
case obj: PlanetSideServerObject with JammableUnit if obj.Jammed =>
|
case obj: PlanetSideServerObject with JammableUnit if obj.Jammed =>
|
||||||
obj.Zone.VehicleEvents ! VehicleServiceMessage(
|
obj.Zone.VehicleEvents ! VehicleServiceMessage(
|
||||||
obj.Zone.Id,
|
obj.Zone.id,
|
||||||
VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, obj.GUID, 27, 0)
|
VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, obj.GUID, 27, 0)
|
||||||
)
|
)
|
||||||
case _ => ;
|
case _ => ;
|
||||||
|
|
@ -154,7 +154,7 @@ object ShieldGeneratorControl {
|
||||||
if (damageToShields) {
|
if (damageToShields) {
|
||||||
val zone = target.Zone
|
val zone = target.Zone
|
||||||
zone.VehicleEvents ! VehicleServiceMessage(
|
zone.VehicleEvents ! VehicleServiceMessage(
|
||||||
zone.Id,
|
zone.id,
|
||||||
VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, target.GUID, 68, target.Shields)
|
VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, target.GUID, 68, target.Shields)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -111,11 +111,11 @@ object SpawnPoint {
|
||||||
val ori = target.Orientation
|
val ori = target.Orientation
|
||||||
val zrad = math.toRadians(ori.z)
|
val zrad = math.toRadians(ori.z)
|
||||||
val radius =
|
val radius =
|
||||||
scala.math.random.toFloat * d.UseRadius / 2 + 20f //20 is definitely outside of the gating energy field
|
scala.math.random().toFloat * d.UseRadius / 2 + 20f //20 is definitely outside of the gating energy field
|
||||||
val shift = Vector3(math.sin(zrad).toFloat, math.cos(zrad).toFloat, 0) * radius
|
val shift = Vector3(math.sin(zrad).toFloat, math.cos(zrad).toFloat, 0) * radius
|
||||||
val altitudeShift = target.Definition match {
|
val altitudeShift = target.Definition match {
|
||||||
case vdef: VehicleDefinition if GlobalDefinitions.isFlightVehicle(vdef) =>
|
case vdef: VehicleDefinition if GlobalDefinitions.isFlightVehicle(vdef) =>
|
||||||
Vector3.z(scala.math.random.toFloat * d.UseRadius / 4 + 20f)
|
Vector3.z(scala.math.random().toFloat * d.UseRadius / 4 + 20f)
|
||||||
case _ =>
|
case _ =>
|
||||||
Vector3.Zero
|
Vector3.Zero
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -145,7 +145,7 @@ object Tool {
|
||||||
if (!iter.hasNext) {
|
if (!iter.hasNext) {
|
||||||
list
|
list
|
||||||
} else {
|
} else {
|
||||||
val index = iter.next
|
val index = iter.next()
|
||||||
fmodes.filter(fmode => fmode.AmmoSlotIndex == index) match {
|
fmodes.filter(fmode => fmode.AmmoSlotIndex == index) match {
|
||||||
case fmode :: _ =>
|
case fmode :: _ =>
|
||||||
buildFireModes(tdef, iter, fmodes, list :+ new FireModeSlot(tdef, fmode))
|
buildFireModes(tdef, iter, fmodes, list :+ new FireModeSlot(tdef, fmode))
|
||||||
|
|
|
||||||
|
|
@ -199,16 +199,16 @@ class Vehicle(private val vehicleDef: VehicleDefinition)
|
||||||
Flying
|
Flying
|
||||||
}
|
}
|
||||||
|
|
||||||
def NtuCapacitorScaled : Int = {
|
def NtuCapacitorScaled: Int = {
|
||||||
if(Definition.MaxNtuCapacitor > 0) {
|
if (Definition.MaxNtuCapacitor > 0) {
|
||||||
scala.math.ceil((NtuCapacitor.toFloat / Definition.MaxNtuCapacitor.toFloat) * 10).toInt
|
scala.math.ceil((NtuCapacitor.toFloat / Definition.MaxNtuCapacitor.toFloat) * 10).toInt
|
||||||
} else {
|
} else {
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def Capacitor : Int = capacitor
|
def Capacitor: Int = capacitor
|
||||||
|
|
||||||
def Capacitor_=(value: Int): Int = {
|
def Capacitor_=(value: Int): Int = {
|
||||||
if (value > Definition.MaxCapacitor) {
|
if (value > Definition.MaxCapacitor) {
|
||||||
capacitor = Definition.MaxCapacitor
|
capacitor = Definition.MaxCapacitor
|
||||||
|
|
@ -365,7 +365,7 @@ class Vehicle(private val vehicleDef: VehicleDefinition)
|
||||||
if (!iter.hasNext) {
|
if (!iter.hasNext) {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
val (seatNumber, seat) = iter.next
|
val (seatNumber, seat) = iter.next()
|
||||||
if (seat.Occupant.contains(player)) {
|
if (seat.Occupant.contains(player)) {
|
||||||
Some(seatNumber)
|
Some(seatNumber)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -36,10 +36,10 @@ object Vehicles {
|
||||||
def Own(vehicle: Vehicle, playerOpt: Option[Player]): Option[Vehicle] = {
|
def Own(vehicle: Vehicle, playerOpt: Option[Player]): Option[Vehicle] = {
|
||||||
playerOpt match {
|
playerOpt match {
|
||||||
case Some(tplayer) =>
|
case Some(tplayer) =>
|
||||||
tplayer.VehicleOwned = vehicle.GUID
|
tplayer.avatar.vehicle = Some(vehicle.GUID)
|
||||||
vehicle.AssignOwnership(playerOpt)
|
vehicle.AssignOwnership(playerOpt)
|
||||||
vehicle.Zone.VehicleEvents ! VehicleServiceMessage(
|
vehicle.Zone.VehicleEvents ! VehicleServiceMessage(
|
||||||
vehicle.Zone.Id,
|
vehicle.Zone.id,
|
||||||
VehicleAction.Ownership(tplayer.GUID, vehicle.GUID)
|
VehicleAction.Ownership(tplayer.GUID, vehicle.GUID)
|
||||||
)
|
)
|
||||||
Vehicles.ReloadAccessPermissions(vehicle, tplayer.Name)
|
Vehicles.ReloadAccessPermissions(vehicle, tplayer.Name)
|
||||||
|
|
@ -50,8 +50,9 @@ object Vehicles {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Disassociate a vehicle fromthe player who owns it.
|
* Disassociate a vehicle from the player who owns it.
|
||||||
* @param guid the unique identifier for that vehicle
|
*
|
||||||
|
* @param guid the unique identifier for that vehicle
|
||||||
* @param vehicle the vehicle
|
* @param vehicle the vehicle
|
||||||
* @return the vehicle, if it had a previous owner;
|
* @return the vehicle, if it had a previous owner;
|
||||||
* `None`, otherwise
|
* `None`, otherwise
|
||||||
|
|
@ -59,8 +60,8 @@ object Vehicles {
|
||||||
def Disown(guid: PlanetSideGUID, vehicle: Vehicle): Option[Vehicle] =
|
def Disown(guid: PlanetSideGUID, vehicle: Vehicle): Option[Vehicle] =
|
||||||
vehicle.Zone.GUID(vehicle.Owner) match {
|
vehicle.Zone.GUID(vehicle.Owner) match {
|
||||||
case Some(player: Player) =>
|
case Some(player: Player) =>
|
||||||
if (player.VehicleOwned.contains(guid)) {
|
if (player.avatar.vehicle.contains(guid)) {
|
||||||
player.VehicleOwned = None
|
player.avatar.vehicle = None
|
||||||
vehicle.Zone.VehicleEvents ! VehicleServiceMessage(
|
vehicle.Zone.VehicleEvents ! VehicleServiceMessage(
|
||||||
player.Name,
|
player.Name,
|
||||||
VehicleAction.Ownership(player.GUID, PlanetSideGUID(0))
|
VehicleAction.Ownership(player.GUID, PlanetSideGUID(0))
|
||||||
|
|
@ -99,9 +100,9 @@ object Vehicles {
|
||||||
* @param player the player
|
* @param player the player
|
||||||
*/
|
*/
|
||||||
def Disown(player: Player, zoneOpt: Option[Zone]): Option[Vehicle] = {
|
def Disown(player: Player, zoneOpt: Option[Zone]): Option[Vehicle] = {
|
||||||
player.VehicleOwned match {
|
player.avatar.vehicle match {
|
||||||
case Some(vehicle_guid) =>
|
case Some(vehicle_guid) =>
|
||||||
player.VehicleOwned = None
|
player.avatar.vehicle = None
|
||||||
zoneOpt.getOrElse(player.Zone).GUID(vehicle_guid) match {
|
zoneOpt.getOrElse(player.Zone).GUID(vehicle_guid) match {
|
||||||
case Some(vehicle: Vehicle) =>
|
case Some(vehicle: Vehicle) =>
|
||||||
Disown(player, vehicle)
|
Disown(player, vehicle)
|
||||||
|
|
@ -231,7 +232,7 @@ object Vehicles {
|
||||||
cargoHold.Occupant match {
|
cargoHold.Occupant match {
|
||||||
case Some(cargo: Vehicle) => {
|
case Some(cargo: Vehicle) => {
|
||||||
cargo.Seats(0).Occupant match {
|
cargo.Seats(0).Occupant match {
|
||||||
case Some(cargoDriver : Player) =>
|
case Some(cargoDriver: Player) =>
|
||||||
CargoBehavior.HandleVehicleCargoDismount(
|
CargoBehavior.HandleVehicleCargoDismount(
|
||||||
target.Zone,
|
target.Zone,
|
||||||
cargo.GUID,
|
cargo.GUID,
|
||||||
|
|
@ -255,7 +256,7 @@ object Vehicles {
|
||||||
tplayer.VehicleSeated = None
|
tplayer.VehicleSeated = None
|
||||||
if (tplayer.HasGUID) {
|
if (tplayer.HasGUID) {
|
||||||
zone.VehicleEvents ! VehicleServiceMessage(
|
zone.VehicleEvents ! VehicleServiceMessage(
|
||||||
zone.Id,
|
zone.id,
|
||||||
VehicleAction.KickPassenger(tplayer.GUID, 4, unk2 = false, target.GUID)
|
VehicleAction.KickPassenger(tplayer.GUID, 4, unk2 = false, target.GUID)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -268,7 +269,7 @@ object Vehicles {
|
||||||
target.Actor ! Vehicle.Deconstruct()
|
target.Actor ! Vehicle.Deconstruct()
|
||||||
} else { // Otherwise handle ownership transfer as normal
|
} else { // Otherwise handle ownership transfer as normal
|
||||||
// Remove ownership of our current vehicle, if we have one
|
// Remove ownership of our current vehicle, if we have one
|
||||||
hacker.VehicleOwned match {
|
hacker.avatar.vehicle match {
|
||||||
case Some(guid: PlanetSideGUID) =>
|
case Some(guid: PlanetSideGUID) =>
|
||||||
zone.GUID(guid) match {
|
zone.GUID(guid) match {
|
||||||
case Some(vehicle: Vehicle) =>
|
case Some(vehicle: Vehicle) =>
|
||||||
|
|
@ -294,12 +295,12 @@ object Vehicles {
|
||||||
//todo: Send HackMessage -> HackCleared to vehicle? can be found in packet captures. Not sure if necessary.
|
//todo: Send HackMessage -> HackCleared to vehicle? can be found in packet captures. Not sure if necessary.
|
||||||
// And broadcast the faction change to other clients
|
// And broadcast the faction change to other clients
|
||||||
zone.AvatarEvents ! AvatarServiceMessage(
|
zone.AvatarEvents ! AvatarServiceMessage(
|
||||||
zone.Id,
|
zone.id,
|
||||||
AvatarAction.SetEmpire(Service.defaultPlayerGUID, target.GUID, hacker.Faction)
|
AvatarAction.SetEmpire(Service.defaultPlayerGUID, target.GUID, hacker.Faction)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
zone.LocalEvents ! LocalServiceMessage(
|
zone.LocalEvents ! LocalServiceMessage(
|
||||||
zone.Id,
|
zone.id,
|
||||||
LocalAction.TriggerSound(hacker.GUID, TriggeredSound.HackVehicle, target.Position, 30, 0.49803925f)
|
LocalAction.TriggerSound(hacker.GUID, TriggeredSound.HackVehicle, target.Position, 30, 0.49803925f)
|
||||||
)
|
)
|
||||||
// Clean up after specific vehicles, e.g. remove router telepads
|
// Clean up after specific vehicles, e.g. remove router telepads
|
||||||
|
|
@ -313,31 +314,41 @@ object Vehicles {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def FindANTChargingSource(obj : TransferContainer, ntuChargingTarget : Option[TransferContainer]) : Option[TransferContainer] = {
|
def FindANTChargingSource(
|
||||||
|
obj: TransferContainer,
|
||||||
|
ntuChargingTarget: Option[TransferContainer]
|
||||||
|
): Option[TransferContainer] = {
|
||||||
//determine if we are close enough to charge from something
|
//determine if we are close enough to charge from something
|
||||||
(ntuChargingTarget match {
|
(ntuChargingTarget match {
|
||||||
case Some(target : WarpGate) if {
|
case Some(target: WarpGate) if {
|
||||||
val soiRadius = target.Definition.SOIRadius
|
val soiRadius = target.Definition.SOIRadius
|
||||||
Vector3.DistanceSquared(obj.Position.xy, target.Position.xy) < soiRadius * soiRadius
|
Vector3.DistanceSquared(obj.Position.xy, target.Position.xy) < soiRadius * soiRadius
|
||||||
} =>
|
} =>
|
||||||
Some(target.asInstanceOf[NtuContainer])
|
Some(target.asInstanceOf[NtuContainer])
|
||||||
case None =>
|
case None =>
|
||||||
None
|
None
|
||||||
}).orElse {
|
}).orElse {
|
||||||
val position = obj.Position.xy
|
val position = obj.Position.xy
|
||||||
obj.Zone.Buildings.values
|
obj.Zone.Buildings.values
|
||||||
.collectFirst { case gate : WarpGate
|
.collectFirst {
|
||||||
if { val soiRadius = gate.Definition.SOIRadius
|
case gate: WarpGate if {
|
||||||
Vector3.DistanceSquared(position, gate.Position.xy) < soiRadius * soiRadius } => gate }
|
val soiRadius = gate.Definition.SOIRadius
|
||||||
|
Vector3.DistanceSquared(position, gate.Position.xy) < soiRadius * soiRadius
|
||||||
|
} =>
|
||||||
|
gate
|
||||||
|
}
|
||||||
.asInstanceOf[Option[NtuContainer]]
|
.asInstanceOf[Option[NtuContainer]]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def FindANTDischargingTarget(obj : TransferContainer, ntuChargingTarget : Option[TransferContainer]) : Option[TransferContainer] = {
|
def FindANTDischargingTarget(
|
||||||
|
obj: TransferContainer,
|
||||||
|
ntuChargingTarget: Option[TransferContainer]
|
||||||
|
): Option[TransferContainer] = {
|
||||||
(ntuChargingTarget match {
|
(ntuChargingTarget match {
|
||||||
case out @ Some(target : NtuContainer) if {
|
case out @ Some(target: NtuContainer) if {
|
||||||
Vector3.DistanceSquared(obj.Position.xy, target.Position.xy) < 400 //20m is generous ...
|
Vector3.DistanceSquared(obj.Position.xy, target.Position.xy) < 400 //20m is generous ...
|
||||||
} =>
|
} =>
|
||||||
out
|
out
|
||||||
case _ =>
|
case _ =>
|
||||||
None
|
None
|
||||||
|
|
@ -352,8 +363,8 @@ object Vehicles {
|
||||||
} match {
|
} match {
|
||||||
case Some(building) =>
|
case Some(building) =>
|
||||||
building.Amenities
|
building.Amenities
|
||||||
.collect { case obj : NtuContainer => obj }
|
.collect { case obj: NtuContainer => obj }
|
||||||
.sortBy {o => Vector3.DistanceSquared(position, o.Position.xy) < 400 } //20m is generous ...
|
.sortBy { o => Vector3.DistanceSquared(position, o.Position.xy) < 400 } //20m is generous ...
|
||||||
.headOption
|
.headOption
|
||||||
case None =>
|
case None =>
|
||||||
None
|
None
|
||||||
|
|
@ -363,9 +374,10 @@ object Vehicles {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Before a vehicle is removed from the game world, the following actions must be performed.
|
* Before a vehicle is removed from the game world, the following actions must be performed.
|
||||||
|
*
|
||||||
* @param vehicle the vehicle
|
* @param vehicle the vehicle
|
||||||
*/
|
*/
|
||||||
def BeforeUnloadVehicle(vehicle : Vehicle, zone : Zone) : Unit = {
|
def BeforeUnloadVehicle(vehicle: Vehicle, zone: Zone): Unit = {
|
||||||
vehicle.Definition match {
|
vehicle.Definition match {
|
||||||
case GlobalDefinitions.ams =>
|
case GlobalDefinitions.ams =>
|
||||||
vehicle.Actor ! Deployment.TryUndeploy(DriveState.Undeploying)
|
vehicle.Actor ! Deployment.TryUndeploy(DriveState.Undeploying)
|
||||||
|
|
@ -377,17 +389,17 @@ object Vehicles {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def RemoveTelepads(vehicle: Vehicle) : Unit = {
|
def RemoveTelepads(vehicle: Vehicle): Unit = {
|
||||||
val zone = vehicle.Zone
|
val zone = vehicle.Zone
|
||||||
(vehicle.Utility(UtilityType.internal_router_telepad_deployable) match {
|
(vehicle.Utility(UtilityType.internal_router_telepad_deployable) match {
|
||||||
case Some(util : Utility.InternalTelepad) =>
|
case Some(util: Utility.InternalTelepad) =>
|
||||||
val telepad = util.Telepad
|
val telepad = util.Telepad
|
||||||
util.Telepad = None
|
util.Telepad = None
|
||||||
zone.GUID(telepad)
|
zone.GUID(telepad)
|
||||||
case _ =>
|
case _ =>
|
||||||
None
|
None
|
||||||
}) match {
|
}) match {
|
||||||
case Some(telepad : TelepadDeployable) =>
|
case Some(telepad: TelepadDeployable) =>
|
||||||
log.debug(s"BeforeUnload: deconstructing telepad $telepad that was linked to router $vehicle ...")
|
log.debug(s"BeforeUnload: deconstructing telepad $telepad that was linked to router $vehicle ...")
|
||||||
telepad.Active = false
|
telepad.Active = false
|
||||||
zone.LocalEvents ! LocalServiceMessage.Deployables(RemoverActor.ClearSpecific(List(telepad), zone))
|
zone.LocalEvents ! LocalServiceMessage.Deployables(RemoverActor.ClearSpecific(List(telepad), zone))
|
||||||
|
|
|
||||||
188
common/src/main/scala/net/psforever/objects/avatar/Avatar.scala
Normal file
188
common/src/main/scala/net/psforever/objects/avatar/Avatar.scala
Normal file
|
|
@ -0,0 +1,188 @@
|
||||||
|
package net.psforever.objects.avatar
|
||||||
|
|
||||||
|
import net.psforever.objects.definition.{AvatarDefinition, BasicDefinition}
|
||||||
|
import net.psforever.objects.equipment.{EquipmentSize, EquipmentSlot}
|
||||||
|
import net.psforever.objects.loadouts.{Loadout, SquadLoadout}
|
||||||
|
import net.psforever.objects.{GlobalDefinitions, LockerContainer, LockerEquipment, OffhandEquipmentSlot}
|
||||||
|
import net.psforever.types._
|
||||||
|
import org.joda.time.{LocalDateTime, Period}
|
||||||
|
import scala.collection.immutable.Seq
|
||||||
|
import scala.concurrent.duration.{FiniteDuration, _}
|
||||||
|
|
||||||
|
object Avatar {
|
||||||
|
val purchaseCooldowns: Map[BasicDefinition, FiniteDuration] = Map(
|
||||||
|
GlobalDefinitions.ams -> 5.minutes,
|
||||||
|
GlobalDefinitions.ant -> 5.minutes,
|
||||||
|
GlobalDefinitions.apc_nc -> 5.minutes,
|
||||||
|
GlobalDefinitions.apc_tr -> 5.minutes,
|
||||||
|
GlobalDefinitions.apc_vs -> 5.minutes,
|
||||||
|
GlobalDefinitions.aurora -> 5.minutes,
|
||||||
|
GlobalDefinitions.battlewagon -> 5.minutes,
|
||||||
|
GlobalDefinitions.dropship -> 5.minutes,
|
||||||
|
GlobalDefinitions.flail -> 5.minutes,
|
||||||
|
GlobalDefinitions.fury -> 5.minutes,
|
||||||
|
GlobalDefinitions.galaxy_gunship -> 10.minutes,
|
||||||
|
GlobalDefinitions.lodestar -> 5.minutes,
|
||||||
|
GlobalDefinitions.liberator -> 5.minutes,
|
||||||
|
GlobalDefinitions.lightgunship -> 5.minutes,
|
||||||
|
GlobalDefinitions.lightning -> 5.minutes,
|
||||||
|
GlobalDefinitions.magrider -> 5.minutes,
|
||||||
|
GlobalDefinitions.mediumtransport -> 5.minutes,
|
||||||
|
GlobalDefinitions.mosquito -> 5.minutes,
|
||||||
|
GlobalDefinitions.phantasm -> 5.minutes,
|
||||||
|
GlobalDefinitions.prowler -> 5.minutes,
|
||||||
|
GlobalDefinitions.quadassault -> 5.minutes,
|
||||||
|
GlobalDefinitions.quadstealth -> 5.minutes,
|
||||||
|
GlobalDefinitions.router -> 5.minutes,
|
||||||
|
GlobalDefinitions.switchblade -> 5.minutes,
|
||||||
|
GlobalDefinitions.skyguard -> 5.minutes,
|
||||||
|
GlobalDefinitions.threemanheavybuggy -> 5.minutes,
|
||||||
|
GlobalDefinitions.thunderer -> 5.minutes,
|
||||||
|
GlobalDefinitions.two_man_assault_buggy -> 5.minutes,
|
||||||
|
GlobalDefinitions.twomanhoverbuggy -> 5.minutes,
|
||||||
|
GlobalDefinitions.twomanheavybuggy -> 5.minutes,
|
||||||
|
GlobalDefinitions.vanguard -> 5.minutes,
|
||||||
|
GlobalDefinitions.vulture -> 5.minutes,
|
||||||
|
GlobalDefinitions.wasp -> 5.minutes,
|
||||||
|
GlobalDefinitions.flamethrower -> 3.minutes,
|
||||||
|
GlobalDefinitions.VSMAX -> 5.minutes,
|
||||||
|
GlobalDefinitions.NCMAX -> 5.minutes,
|
||||||
|
GlobalDefinitions.TRMAX -> 5.minutes,
|
||||||
|
// TODO weapon based cooldown
|
||||||
|
GlobalDefinitions.nchev_sparrow -> 5.minutes,
|
||||||
|
GlobalDefinitions.nchev_falcon -> 5.minutes,
|
||||||
|
GlobalDefinitions.nchev_scattercannon -> 5.minutes,
|
||||||
|
GlobalDefinitions.vshev_comet -> 5.minutes,
|
||||||
|
GlobalDefinitions.vshev_quasar -> 5.minutes,
|
||||||
|
GlobalDefinitions.vshev_starfire -> 5.minutes,
|
||||||
|
GlobalDefinitions.trhev_burster -> 5.minutes,
|
||||||
|
GlobalDefinitions.trhev_dualcycler -> 5.minutes,
|
||||||
|
GlobalDefinitions.trhev_pounder -> 5.minutes
|
||||||
|
)
|
||||||
|
|
||||||
|
val useCooldowns: Map[BasicDefinition, FiniteDuration] = Map(
|
||||||
|
GlobalDefinitions.medkit -> 5.seconds,
|
||||||
|
GlobalDefinitions.super_armorkit -> 20.minutes,
|
||||||
|
GlobalDefinitions.super_medkit -> 20.minutes,
|
||||||
|
GlobalDefinitions.super_staminakit -> 20.minutes
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
case class Avatar(
|
||||||
|
/** unique identifier corresponding to a database table row index */
|
||||||
|
id: Int,
|
||||||
|
name: String,
|
||||||
|
faction: PlanetSideEmpire.Value,
|
||||||
|
sex: CharacterGender.Value,
|
||||||
|
head: Int,
|
||||||
|
voice: CharacterVoice.Value,
|
||||||
|
bep: Long = 0,
|
||||||
|
cep: Long = 0,
|
||||||
|
stamina: Int = 100,
|
||||||
|
fatigued: Boolean = false,
|
||||||
|
cosmetics: Option[Set[Cosmetic]] = None,
|
||||||
|
certifications: Set[Certification] = Set(),
|
||||||
|
loadouts: Seq[Option[Loadout]] = Seq.fill(15)(None),
|
||||||
|
squadLoadouts: Seq[Option[SquadLoadout]] = Seq.fill(10)(None),
|
||||||
|
implants: Seq[Option[Implant]] = Seq(None, None, None),
|
||||||
|
locker: LockerContainer = new LockerContainer(), // TODO var bad
|
||||||
|
deployables: DeployableToolbox = new DeployableToolbox(), // TODO var bad
|
||||||
|
lookingForSquad: Boolean = false,
|
||||||
|
var vehicle: Option[PlanetSideGUID] = None, // TODO var bad
|
||||||
|
firstTimeEvents: Set[String] =
|
||||||
|
FirstTimeEvents.Maps ++ FirstTimeEvents.Monoliths ++
|
||||||
|
FirstTimeEvents.Standard.All ++ FirstTimeEvents.Cavern.All ++
|
||||||
|
FirstTimeEvents.TR.All ++ FirstTimeEvents.NC.All ++ FirstTimeEvents.VS.All ++
|
||||||
|
FirstTimeEvents.Generic,
|
||||||
|
/** Timestamps of when a vehicle or equipment was last purchased */
|
||||||
|
purchaseTimes: Map[String, LocalDateTime] = Map(),
|
||||||
|
/** Timestamps of when a vehicle or equipment was last purchased */
|
||||||
|
useTimes: Map[String, LocalDateTime] = Map()
|
||||||
|
) {
|
||||||
|
assert(bep >= 0)
|
||||||
|
assert(cep >= 0)
|
||||||
|
|
||||||
|
val br: BattleRank = BattleRank.withExperience(bep)
|
||||||
|
val cr: CommandRank = CommandRank.withExperience(cep)
|
||||||
|
|
||||||
|
private def cooldown(
|
||||||
|
times: Map[String, LocalDateTime],
|
||||||
|
cooldowns: Map[BasicDefinition, FiniteDuration],
|
||||||
|
definition: BasicDefinition
|
||||||
|
): Option[Period] = {
|
||||||
|
times.get(definition.Name) match {
|
||||||
|
case Some(purchaseTime) =>
|
||||||
|
val secondsSincePurchase = new Period(purchaseTime, LocalDateTime.now()).toStandardSeconds.getSeconds
|
||||||
|
cooldowns.get(definition) match {
|
||||||
|
case Some(cooldown) if (cooldown.toSeconds - secondsSincePurchase) > 0 =>
|
||||||
|
Some(Period.seconds(cooldown.toSeconds.toInt - secondsSincePurchase))
|
||||||
|
case _ => None
|
||||||
|
}
|
||||||
|
case None =>
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns the remaining purchase cooldown or None if an object is not on cooldown */
|
||||||
|
def purchaseCooldown(definition: BasicDefinition): Option[Period] = {
|
||||||
|
cooldown(purchaseTimes, Avatar.purchaseCooldowns, definition)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns the remaining use cooldown or None if an object is not on cooldown */
|
||||||
|
def useCooldown(definition: BasicDefinition): Option[Period] = {
|
||||||
|
cooldown(useTimes, Avatar.useCooldowns, definition)
|
||||||
|
}
|
||||||
|
|
||||||
|
def fifthSlot(): EquipmentSlot = {
|
||||||
|
new OffhandEquipmentSlot(EquipmentSize.Inventory) {
|
||||||
|
val obj = new LockerEquipment(locker)
|
||||||
|
Equipment = obj
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val definition: AvatarDefinition = GlobalDefinitions.avatar
|
||||||
|
|
||||||
|
/** Returns numerical value from 0-3 that is the hacking skill level representation in packets */
|
||||||
|
def hackingSkillLevel(): Int = {
|
||||||
|
if (
|
||||||
|
certifications.contains(Certification.ExpertHacking) || certifications.contains(Certification.ElectronicsExpert)
|
||||||
|
) {
|
||||||
|
3
|
||||||
|
} else if (certifications.contains(Certification.AdvancedHacking)) {
|
||||||
|
2
|
||||||
|
} else if (certifications.contains(Certification.Hacking)) {
|
||||||
|
1
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** The maximum stamina amount */
|
||||||
|
val maxStamina: Int = 100
|
||||||
|
|
||||||
|
/** Return true if the stamina is at the maximum amount */
|
||||||
|
def staminaFull: Boolean = {
|
||||||
|
stamina == maxStamina
|
||||||
|
}
|
||||||
|
|
||||||
|
def canEqual(other: Any): Boolean = other.isInstanceOf[Avatar]
|
||||||
|
|
||||||
|
override def equals(other: Any): Boolean =
|
||||||
|
other match {
|
||||||
|
case that: Avatar =>
|
||||||
|
(that canEqual this) &&
|
||||||
|
id == that.id
|
||||||
|
case _ =>
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Avatar assertions
|
||||||
|
* These protect against programming errors by asserting avatar properties have correct values
|
||||||
|
* They may or may not be disabled for live applications
|
||||||
|
*/
|
||||||
|
assert(stamina <= maxStamina && stamina >= 0)
|
||||||
|
assert(head >= 0) // TODO what's the max value?
|
||||||
|
assert(implants.length <= 3)
|
||||||
|
assert(implants.flatten.map(_.definition.implantType).distinct.length == implants.flatten.length)
|
||||||
|
assert(br.implantSlots >= implants.flatten.length)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,106 @@
|
||||||
|
package net.psforever.objects.avatar
|
||||||
|
|
||||||
|
import enumeratum.values.{IntEnum, IntEnumEntry}
|
||||||
|
import net.psforever.packet.game.objectcreate.UniformStyle
|
||||||
|
|
||||||
|
/** Battle ranks and their starting experience values
|
||||||
|
* Source: http://wiki.psforever.net/wiki/Battle_Rank
|
||||||
|
*/
|
||||||
|
sealed abstract class BattleRank(val value: Int, val experience: Long) extends IntEnumEntry {
|
||||||
|
def implantSlots: Int = {
|
||||||
|
if (this.value >= BattleRank.BR18.value) {
|
||||||
|
3
|
||||||
|
} else if (this.value >= BattleRank.BR12.value) {
|
||||||
|
2
|
||||||
|
} else if (this.value >= BattleRank.BR6.value) {
|
||||||
|
1
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def uniformStyle: UniformStyle.Value = {
|
||||||
|
if (this.value >= BattleRank.BR25.value) {
|
||||||
|
UniformStyle.ThirdUpgrade
|
||||||
|
} else if (this.value >= BattleRank.BR14.value) {
|
||||||
|
UniformStyle.SecondUpgrade
|
||||||
|
} else if (this.value >= BattleRank.BR7.value) {
|
||||||
|
UniformStyle.FirstUpgrade
|
||||||
|
} else {
|
||||||
|
UniformStyle.Normal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
case object BattleRank extends IntEnum[BattleRank] {
|
||||||
|
|
||||||
|
case object BR1 extends BattleRank(value = 1, experience = 0L)
|
||||||
|
|
||||||
|
case object BR2 extends BattleRank(value = 2, experience = 1000L)
|
||||||
|
|
||||||
|
case object BR3 extends BattleRank(value = 3, experience = 3000L)
|
||||||
|
|
||||||
|
case object BR4 extends BattleRank(value = 4, experience = 7500L)
|
||||||
|
|
||||||
|
case object BR5 extends BattleRank(value = 5, experience = 15000L)
|
||||||
|
|
||||||
|
case object BR6 extends BattleRank(value = 6, experience = 30000L)
|
||||||
|
|
||||||
|
case object BR7 extends BattleRank(value = 7, experience = 45000L)
|
||||||
|
|
||||||
|
case object BR8 extends BattleRank(value = 8, experience = 67500L)
|
||||||
|
|
||||||
|
case object BR9 extends BattleRank(value = 9, experience = 101250L)
|
||||||
|
|
||||||
|
case object BR10 extends BattleRank(value = 10, experience = 126563L)
|
||||||
|
|
||||||
|
case object BR11 extends BattleRank(value = 11, experience = 158203L)
|
||||||
|
|
||||||
|
case object BR12 extends BattleRank(value = 12, experience = 197754L)
|
||||||
|
|
||||||
|
case object BR13 extends BattleRank(value = 13, experience = 247192L)
|
||||||
|
|
||||||
|
case object BR14 extends BattleRank(value = 14, experience = 308990L)
|
||||||
|
|
||||||
|
case object BR15 extends BattleRank(value = 15, experience = 386239L)
|
||||||
|
|
||||||
|
case object BR16 extends BattleRank(value = 16, experience = 482798L)
|
||||||
|
|
||||||
|
case object BR17 extends BattleRank(value = 17, experience = 603497L)
|
||||||
|
|
||||||
|
case object BR18 extends BattleRank(value = 18, experience = 754371L)
|
||||||
|
|
||||||
|
case object BR19 extends BattleRank(value = 19, experience = 942964L)
|
||||||
|
|
||||||
|
case object BR20 extends BattleRank(value = 20, experience = 1178705L)
|
||||||
|
|
||||||
|
case object BR21 extends BattleRank(value = 21, experience = 1438020L)
|
||||||
|
|
||||||
|
case object BR22 extends BattleRank(value = 22, experience = 1710301L)
|
||||||
|
|
||||||
|
case object BR23 extends BattleRank(value = 23, experience = 1988027L)
|
||||||
|
|
||||||
|
case object BR24 extends BattleRank(value = 24, experience = 2286231L)
|
||||||
|
|
||||||
|
case object BR25 extends BattleRank(value = 25, experience = 2583441L)
|
||||||
|
|
||||||
|
val values: IndexedSeq[BattleRank] = findValues
|
||||||
|
|
||||||
|
/** Find BattleRank variant for given experience value */
|
||||||
|
def withExperience(experience: Long): BattleRank = {
|
||||||
|
withExperienceOpt(experience).get
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Find BattleRank variant for given experience value */
|
||||||
|
def withExperienceOpt(experience: Long): Option[BattleRank] = {
|
||||||
|
values.find(br =>
|
||||||
|
this.withValueOpt(br.value + 1) match {
|
||||||
|
case Some(nextBr) =>
|
||||||
|
experience >= br.experience && experience < nextBr.experience
|
||||||
|
case None =>
|
||||||
|
experience >= br.experience
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,224 +1,219 @@
|
||||||
// Copyright (c) 2017 PSForever
|
|
||||||
package net.psforever.objects.avatar
|
package net.psforever.objects.avatar
|
||||||
|
|
||||||
import net.psforever.types.CertificationType
|
import enumeratum.values.{IntEnum, IntEnumEntry}
|
||||||
|
import net.psforever.packet.PacketHelpers
|
||||||
|
import scodec.Codec
|
||||||
|
import scodec.codecs._
|
||||||
|
|
||||||
import scala.collection.mutable
|
import scala.annotation.tailrec
|
||||||
|
|
||||||
object Certification {
|
sealed abstract class Certification(
|
||||||
object Dependencies {
|
val value: Int,
|
||||||
|
/** Name used in packets */
|
||||||
|
val name: String,
|
||||||
|
/** Certification point cost */
|
||||||
|
val cost: Int,
|
||||||
|
val requires: Set[Certification] = Set(),
|
||||||
|
val replaces: Set[Certification] = Set()
|
||||||
|
) extends IntEnumEntry
|
||||||
|
|
||||||
/**
|
case object Certification extends IntEnum[Certification] {
|
||||||
* Find the certifications that are immediately dependent on the target certification.
|
|
||||||
* (For `A`, find all `B` that are `B ⇒ A`.)
|
|
||||||
* @param certification the target certification
|
|
||||||
* @return all connected certifications
|
|
||||||
*/
|
|
||||||
def From(certification: CertificationType.Value): Set[CertificationType.Value] = dependencies(certification).toSet
|
|
||||||
|
|
||||||
/**
|
case object StandardAssault extends Certification(value = 0, name = "standard_assault", cost = 0)
|
||||||
* Find all certifications that are dependent on the target certification.
|
|
||||||
* (For `A`, find all `B...C` where `C ⇒ B` and `B ⇒ A`.)
|
|
||||||
* @param certification the target certification
|
|
||||||
* @return all connected certifications
|
|
||||||
*/
|
|
||||||
def FromAll(certification: CertificationType.Value): Set[CertificationType.Value] = {
|
|
||||||
var available: List[CertificationType.Value] = List(certification)
|
|
||||||
var allocated: mutable.ListBuffer[CertificationType.Value] = mutable.ListBuffer.empty[CertificationType.Value]
|
|
||||||
do {
|
|
||||||
available = available.flatMap(cert => dependencies(cert))
|
|
||||||
allocated ++= available
|
|
||||||
} while (available.nonEmpty)
|
|
||||||
allocated.toSet
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
case object MediumAssault extends Certification(value = 1, name = "medium_assault", cost = 2)
|
||||||
* Find the certifications that are immediate dependencies of the target certification.
|
|
||||||
* (For `A`, find all `B` where `A ⇒ B`.)
|
|
||||||
* @param certification the target certification
|
|
||||||
* @return all connected certifications
|
|
||||||
*/
|
|
||||||
def For(certification: CertificationType.Value): Set[CertificationType.Value] = {
|
|
||||||
(for {
|
|
||||||
(cert, certs) <- dependencies
|
|
||||||
if certs contains certification
|
|
||||||
} yield cert).toSet
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
case object HeavyAssault
|
||||||
* Find all certifications that are dependencies of the target certification.
|
extends Certification(value = 2, name = "heavy_assault", cost = 4, requires = Set(MediumAssault))
|
||||||
* (For `A`, find all `B...C` where `A ⇒ B` and `B ⇒ C`.)
|
|
||||||
* @param certification the target certification
|
|
||||||
* @return all connected certifications
|
|
||||||
*/
|
|
||||||
def ForAll(certification: CertificationType.Value): Set[CertificationType.Value] = {
|
|
||||||
var available: List[CertificationType.Value] = List(certification)
|
|
||||||
var allocated: mutable.ListBuffer[CertificationType.Value] = mutable.ListBuffer.empty[CertificationType.Value]
|
|
||||||
do {
|
|
||||||
available = available.flatMap {
|
|
||||||
For
|
|
||||||
}
|
|
||||||
allocated ++= available
|
|
||||||
} while (available.nonEmpty)
|
|
||||||
allocated.toSet
|
|
||||||
}
|
|
||||||
|
|
||||||
import CertificationType._
|
case object SpecialAssault
|
||||||
|
extends Certification(value = 3, name = "special_assault", cost = 3, requires = Set(MediumAssault))
|
||||||
|
|
||||||
/**
|
case object AntiVehicular
|
||||||
* Find all certifications that are related but mutually exclusive with the target certification.
|
extends Certification(value = 4, name = "anti_vehicular", cost = 3, requires = Set(MediumAssault))
|
||||||
* (For `A`, find all `B` that `B ⊃ A` but `A XOR B`.)
|
|
||||||
* @param certification the target certification
|
|
||||||
* @return all connected certifications
|
|
||||||
*/
|
|
||||||
def Like(certification: CertificationType.Value): Set[CertificationType.Value] =
|
|
||||||
certification match {
|
|
||||||
case AssaultBuggy =>
|
|
||||||
Set(Harasser)
|
|
||||||
case LightScout =>
|
|
||||||
Set(AirCavalryScout, AssaultBuggy, Harasser)
|
|
||||||
case UniMAX =>
|
|
||||||
Set(AAMAX, AIMAX, AVMAX)
|
|
||||||
case AdvancedEngineering =>
|
|
||||||
Set(AssaultEngineering, FortificationEngineering)
|
|
||||||
case ElectronicsExpert =>
|
|
||||||
Set(DataCorruption, ExpertHacking)
|
|
||||||
case _ =>
|
|
||||||
Set.empty[CertificationType.Value]
|
|
||||||
}
|
|
||||||
|
|
||||||
private val dependencies: Map[CertificationType.Value, List[CertificationType.Value]] = Map(
|
case object Sniping extends Certification(value = 5, name = "sniper", cost = 3, requires = Set(MediumAssault))
|
||||||
StandardAssault -> List(),
|
|
||||||
AgileExoSuit -> List(),
|
case object EliteAssault
|
||||||
ReinforcedExoSuit -> List(),
|
extends Certification(value = 6, name = "special_assault_2", cost = 1, requires = Set(SpecialAssault))
|
||||||
InfiltrationSuit -> List(Phantasm),
|
|
||||||
AIMAX -> List(),
|
case object AirCavalryScout extends Certification(value = 7, name = "air_cavalry_scout", cost = 3)
|
||||||
AVMAX -> List(),
|
|
||||||
AAMAX -> List(),
|
case object AirCavalryInterceptor
|
||||||
UniMAX -> List(),
|
extends Certification(value = 8, name = "air_cavalry_interceptor", cost = 2, requires = Set(AirCavalryScout))
|
||||||
StandardAssault -> List(),
|
|
||||||
MediumAssault -> List(AntiVehicular, HeavyAssault, Sniping, SpecialAssault),
|
case object AirCavalryAssault
|
||||||
AntiVehicular -> List(),
|
extends Certification(
|
||||||
HeavyAssault -> List(),
|
value = 9,
|
||||||
Sniping -> List(),
|
name = "air_cavalry_assault",
|
||||||
SpecialAssault -> List(EliteAssault),
|
cost = 2,
|
||||||
EliteAssault -> List(),
|
requires = Set(AirCavalryScout)
|
||||||
ATV -> List(Switchblade),
|
)
|
||||||
Switchblade -> List(),
|
|
||||||
Harasser -> List(),
|
case object AirSupport extends Certification(value = 10, name = "air_support", cost = 3)
|
||||||
AssaultBuggy -> List(),
|
|
||||||
LightScout -> List(AirCavalryAssault),
|
case object ATV extends Certification(value = 11, name = "quad_all", cost = 1)
|
||||||
GroundSupport -> List(),
|
|
||||||
GroundTransport -> List(),
|
case object LightScout
|
||||||
ArmoredAssault1 -> List(ArmoredAssault2),
|
extends Certification(
|
||||||
ArmoredAssault2 -> List(BattleFrameRobotics, Flail),
|
value = 12,
|
||||||
Flail -> List(),
|
name = "light_scout",
|
||||||
AirCavalryScout -> List(AirCavalryAssault),
|
cost = 5,
|
||||||
AirCavalryAssault -> List(AirCavalryInterceptor),
|
replaces = Set(AirCavalryScout, AssaultBuggy, Harasser)
|
||||||
AirCavalryInterceptor -> List(),
|
)
|
||||||
AirSupport -> List(GalaxyGunship),
|
|
||||||
GalaxyGunship -> List(),
|
case object AssaultBuggy extends Certification(value = 13, name = "assault_buggy", cost = 3, replaces = Set(Harasser))
|
||||||
Phantasm -> List(),
|
|
||||||
BattleFrameRobotics -> List(BFRAntiInfantry, BFRAntiAircraft),
|
case object ArmoredAssault1 extends Certification(value = 14, name = "armored_assault1", cost = 2)
|
||||||
BFRAntiInfantry -> List(),
|
|
||||||
BFRAntiAircraft -> List(),
|
case object ArmoredAssault2
|
||||||
Medical -> List(AdvancedMedical),
|
extends Certification(value = 15, name = "armored_assault2", cost = 3, requires = Set(ArmoredAssault1))
|
||||||
AdvancedMedical -> List(),
|
|
||||||
Engineering -> List(CombatEngineering),
|
case object GroundTransport extends Certification(value = 16, name = "ground_transport", cost = 2)
|
||||||
CombatEngineering -> List(AdvancedEngineering, AssaultEngineering, FortificationEngineering),
|
|
||||||
AdvancedEngineering -> List(),
|
case object GroundSupport extends Certification(value = 17, name = "ground_support", cost = 2)
|
||||||
AssaultEngineering -> List(),
|
|
||||||
FortificationEngineering -> List(),
|
case object BattleFrameRobotics
|
||||||
Hacking -> List(AdvancedHacking),
|
extends Certification(value = 18, name = "TODO2", cost = 4, requires = Set(ArmoredAssault2)) // TODO name
|
||||||
AdvancedHacking -> List(DataCorruption, ElectronicsExpert, ExpertHacking),
|
|
||||||
DataCorruption -> List(),
|
case object Flail extends Certification(value = 19, name = "flail", cost = 1, requires = Set(ArmoredAssault2))
|
||||||
ElectronicsExpert -> List(),
|
|
||||||
ExpertHacking -> List()
|
case object Switchblade extends Certification(value = 20, name = "switchblade", cost = 1, requires = Set(ATV))
|
||||||
|
|
||||||
|
case object Harasser extends Certification(value = 21, name = "harasser", cost = 1)
|
||||||
|
|
||||||
|
case object Phantasm extends Certification(value = 22, name = "phantasm", cost = 3, requires = Set(InfiltrationSuit))
|
||||||
|
|
||||||
|
case object GalaxyGunship extends Certification(value = 23, name = "gunship", cost = 2, requires = Set(AirSupport))
|
||||||
|
|
||||||
|
case object BFRAntiAircraft
|
||||||
|
extends Certification(value = 24, name = "TODO3", cost = 1, requires = Set(BattleFrameRobotics))
|
||||||
|
|
||||||
|
case object BFRAntiInfantry
|
||||||
|
extends Certification(value = 25, name = "TODO4", cost = 1, requires = Set(BattleFrameRobotics)) // TODO name
|
||||||
|
|
||||||
|
case object StandardExoSuit extends Certification(value = 26, name = "TODO5", cost = 0)
|
||||||
|
|
||||||
|
case object AgileExoSuit extends Certification(value = 27, name = "agile_armor", cost = 0)
|
||||||
|
|
||||||
|
case object ReinforcedExoSuit extends Certification(value = 28, name = "reinforced_armor", cost = 3)
|
||||||
|
|
||||||
|
case object InfiltrationSuit extends Certification(value = 29, name = "infiltration_suit", cost = 2)
|
||||||
|
|
||||||
|
case object AAMAX extends Certification(value = 30, name = "max_anti_aircraft", cost = 2)
|
||||||
|
|
||||||
|
case object AIMAX extends Certification(value = 31, name = "max_anti_personnel", cost = 3)
|
||||||
|
|
||||||
|
case object AVMAX extends Certification(value = 32, name = "max_anti_vehicular", cost = 3)
|
||||||
|
|
||||||
|
case object UniMAX extends Certification(value = 33, name = "max_all", cost = 6, replaces = Set(AAMAX, AIMAX, AVMAX))
|
||||||
|
|
||||||
|
case object Medical extends Certification(value = 34, name = "Medical", cost = 3)
|
||||||
|
|
||||||
|
case object AdvancedMedical
|
||||||
|
extends Certification(value = 35, name = "advanced_medical", cost = 2, requires = Set(Medical))
|
||||||
|
|
||||||
|
case object Hacking extends Certification(value = 36, name = "Hacking", cost = 3)
|
||||||
|
|
||||||
|
case object AdvancedHacking
|
||||||
|
extends Certification(value = 37, name = "advanced_hacking", cost = 2, requires = Set(Hacking))
|
||||||
|
|
||||||
|
case object ExpertHacking
|
||||||
|
extends Certification(value = 38, name = "expert_hacking", cost = 2, requires = Set(AdvancedHacking))
|
||||||
|
|
||||||
|
case object DataCorruption
|
||||||
|
extends Certification(value = 39, name = "virus_hacking", cost = 3, requires = Set(AdvancedHacking))
|
||||||
|
|
||||||
|
case object ElectronicsExpert
|
||||||
|
extends Certification(
|
||||||
|
value = 40,
|
||||||
|
name = "electronics_expert",
|
||||||
|
cost = 4,
|
||||||
|
requires = Set(AdvancedHacking),
|
||||||
|
replaces = Set(DataCorruption, ExpertHacking)
|
||||||
|
)
|
||||||
|
|
||||||
|
case object Engineering extends Certification(value = 41, name = "Repair", cost = 3)
|
||||||
|
|
||||||
|
case object CombatEngineering
|
||||||
|
extends Certification(value = 42, name = "combat_engineering", cost = 2, requires = Set(Engineering))
|
||||||
|
|
||||||
|
case object FortificationEngineering
|
||||||
|
extends Certification(value = 43, name = "ce_defense", cost = 3, requires = Set(CombatEngineering))
|
||||||
|
|
||||||
|
case object AssaultEngineering
|
||||||
|
extends Certification(value = 44, name = "ce_offense", cost = 3, requires = Set(CombatEngineering))
|
||||||
|
|
||||||
|
case object AdvancedEngineering
|
||||||
|
extends Certification(
|
||||||
|
value = 45,
|
||||||
|
name = "ce_advanced",
|
||||||
|
cost = 5,
|
||||||
|
requires = Set(CombatEngineering),
|
||||||
|
replaces = Set(AssaultEngineering, FortificationEngineering)
|
||||||
|
)
|
||||||
|
|
||||||
|
// https://github.com/lloydmeta/enumeratum/issues/86
|
||||||
|
lazy val values: IndexedSeq[Certification] = findValues
|
||||||
|
|
||||||
|
implicit val codec: Codec[Certification] = PacketHelpers.createIntEnumCodec(this, uint8L)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Certifications are often stored, in object form, as a 46-member collection.
|
||||||
|
* Encode a subset of certification values for packet form.
|
||||||
|
*
|
||||||
|
* @return the certifications, as a single value
|
||||||
|
*/
|
||||||
|
def toEncodedLong(certs: Set[Certification]): Long = {
|
||||||
|
certs
|
||||||
|
.map { cert => math.pow(2, cert.value).toLong }
|
||||||
|
.foldLeft(0L)(_ + _)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Certifications are often stored, in packet form, as an encoded little-endian `46u` value.
|
||||||
|
* Decode a representative value into a subset of certification values.
|
||||||
|
*
|
||||||
|
* @see `ChangeSquadMemberRequirementsCertifications`
|
||||||
|
* @see `changeSquadMemberRequirementsCertificationsCodec`
|
||||||
|
* @see `fromEncodedLong(Long, Iterable[Long], Set[CertificationType.Value])`
|
||||||
|
* @param certs the certifications, as a single value
|
||||||
|
* @return the certifications, as a sequence of values
|
||||||
|
*/
|
||||||
|
def fromEncodedLong(certs: Long): Set[Certification] = {
|
||||||
|
recursiveFromEncodedLong(
|
||||||
|
certs,
|
||||||
|
Certification.values.map { cert => math.pow(2, cert.value).toLong }.sorted
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
object Cost {
|
/**
|
||||||
|
* Certifications are often stored, in packet form, as an encoded little-endian `46u` value.
|
||||||
/**
|
* Decode a representative value into a subset of certification values
|
||||||
* For a certification, get its point cost.
|
* by repeatedly finding the partition point of values less than a specific one,
|
||||||
* @param certification the certification
|
* providing for both the next lowest value (to subtract) and an index (of a certification).
|
||||||
* @return the cost
|
*
|
||||||
*/
|
* @see `ChangeSquadMemberRequirementsCertifications`
|
||||||
def Of(certification: CertificationType.Value): Int = points(certification)
|
* @see `changeSquadMemberRequirementsCertificationsCodec`
|
||||||
|
* @see `fromEncodedLong(Long)`
|
||||||
/**
|
* @param certs the certifications, as a single value
|
||||||
* For a list of certifications, find the point cost of all unique certifications.
|
* @param splitList the available values to partition
|
||||||
* @see `Of(Set)`
|
* @param out the accumulating certification values;
|
||||||
* @param certifications the certification list
|
* defaults to an empty set
|
||||||
* @return the total cost
|
* @return the certifications, as a sequence of values
|
||||||
*/
|
*/
|
||||||
def Of(certifications: List[CertificationType.Value]): Int = Of(certifications.toSet)
|
@tailrec
|
||||||
|
private def recursiveFromEncodedLong(
|
||||||
/**
|
certs: Long,
|
||||||
* For a set of certifications, find the point cost of all certifications.
|
splitList: Iterable[Long],
|
||||||
* @see `OfAll(List)`
|
out: Set[Certification] = Set.empty
|
||||||
* @param certifications the certification list
|
): Set[Certification] = {
|
||||||
* @return the total cost
|
if (certs == 0 || splitList.isEmpty) {
|
||||||
*/
|
out
|
||||||
def Of(certifications: Set[CertificationType.Value]): Int = OfAll(certifications.toList)
|
} else {
|
||||||
|
val (less, _) = splitList.partition(_ <= certs)
|
||||||
/**
|
recursiveFromEncodedLong(certs - less.last, less, out ++ Set(Certification.withValue(less.size - 1)))
|
||||||
* For a list of certifications, find the point cost of all certifications, counting any duplicates.
|
|
||||||
* @param certifications the certification list
|
|
||||||
* @return the total cost
|
|
||||||
*/
|
|
||||||
def OfAll(certifications: List[CertificationType.Value]): Int = {
|
|
||||||
certifications map points sum
|
|
||||||
}
|
}
|
||||||
|
|
||||||
import CertificationType._
|
|
||||||
private val points: Map[CertificationType.Value, Int] = Map(
|
|
||||||
StandardExoSuit -> 0,
|
|
||||||
AgileExoSuit -> 0,
|
|
||||||
ReinforcedExoSuit -> 3,
|
|
||||||
InfiltrationSuit -> 2,
|
|
||||||
AAMAX -> 2,
|
|
||||||
AIMAX -> 3,
|
|
||||||
AVMAX -> 3,
|
|
||||||
UniMAX -> 6,
|
|
||||||
StandardAssault -> 0,
|
|
||||||
MediumAssault -> 2,
|
|
||||||
AntiVehicular -> 3,
|
|
||||||
HeavyAssault -> 4,
|
|
||||||
Sniping -> 3,
|
|
||||||
SpecialAssault -> 3,
|
|
||||||
EliteAssault -> 1,
|
|
||||||
ATV -> 1,
|
|
||||||
Switchblade -> 1,
|
|
||||||
Harasser -> 1,
|
|
||||||
AssaultBuggy -> 3,
|
|
||||||
LightScout -> 5,
|
|
||||||
GroundSupport -> 2,
|
|
||||||
GroundTransport -> 2,
|
|
||||||
ArmoredAssault1 -> 2,
|
|
||||||
ArmoredAssault2 -> 3,
|
|
||||||
Flail -> 1,
|
|
||||||
AirCavalryScout -> 3,
|
|
||||||
AirCavalryAssault -> 2,
|
|
||||||
AirCavalryInterceptor -> 2,
|
|
||||||
AirSupport -> 3,
|
|
||||||
GalaxyGunship -> 2,
|
|
||||||
Phantasm -> 3,
|
|
||||||
BattleFrameRobotics -> 4,
|
|
||||||
BFRAntiInfantry -> 1,
|
|
||||||
BFRAntiAircraft -> 1,
|
|
||||||
Medical -> 3,
|
|
||||||
AdvancedMedical -> 2,
|
|
||||||
Engineering -> 3,
|
|
||||||
CombatEngineering -> 2,
|
|
||||||
AdvancedEngineering -> 5,
|
|
||||||
AssaultEngineering -> 3,
|
|
||||||
FortificationEngineering -> 3,
|
|
||||||
Hacking -> 3,
|
|
||||||
AdvancedHacking -> 2,
|
|
||||||
DataCorruption -> 3,
|
|
||||||
ElectronicsExpert -> 4,
|
|
||||||
ExpertHacking -> 2
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
package net.psforever.objects.avatar
|
||||||
|
|
||||||
|
import enumeratum.values.{IntEnum, IntEnumEntry}
|
||||||
|
|
||||||
|
/** Command ranks and their starting experience values */
|
||||||
|
sealed abstract class CommandRank(val value: Int, val experience: Long) extends IntEnumEntry
|
||||||
|
|
||||||
|
case object CommandRank extends IntEnum[CommandRank] {
|
||||||
|
|
||||||
|
case object CR0 extends CommandRank(value = 0, experience = 0L)
|
||||||
|
|
||||||
|
case object CR1 extends CommandRank(value = 1, experience = 10000L)
|
||||||
|
|
||||||
|
case object CR2 extends CommandRank(value = 2, experience = 50000L)
|
||||||
|
|
||||||
|
case object CR3 extends CommandRank(value = 3, experience = 150000L)
|
||||||
|
|
||||||
|
case object CR4 extends CommandRank(value = 4, experience = 300000L)
|
||||||
|
|
||||||
|
case object CR5 extends CommandRank(value = 5, experience = 600000L)
|
||||||
|
|
||||||
|
val values: IndexedSeq[CommandRank] = findValues
|
||||||
|
|
||||||
|
/** Find CommandRank variant for given experience value */
|
||||||
|
def withExperience(experience: Long): CommandRank = {
|
||||||
|
withExperienceOpt(experience).get
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Find CommandRank variant for given experience value */
|
||||||
|
def withExperienceOpt(experience: Long): Option[CommandRank] = {
|
||||||
|
values.find(cr =>
|
||||||
|
this.withValueOpt(cr.value + 1) match {
|
||||||
|
case Some(nextCr) =>
|
||||||
|
experience >= cr.experience && experience < nextCr.experience
|
||||||
|
case None =>
|
||||||
|
experience >= cr.experience
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -26,7 +26,7 @@ class CorpseControl(player: Player) extends Actor with ContainableBehavior {
|
||||||
obj.Find(item) match {
|
obj.Find(item) match {
|
||||||
case Some(slot) =>
|
case Some(slot) =>
|
||||||
obj.Zone.AvatarEvents ! AvatarServiceMessage(
|
obj.Zone.AvatarEvents ! AvatarServiceMessage(
|
||||||
player.Zone.Id,
|
player.Zone.id,
|
||||||
AvatarAction.SendResponse(Service.defaultPlayerGUID, ObjectAttachMessage(obj.GUID, item.GUID, slot))
|
AvatarAction.SendResponse(Service.defaultPlayerGUID, ObjectAttachMessage(obj.GUID, item.GUID, slot))
|
||||||
)
|
)
|
||||||
case None => ;
|
case None => ;
|
||||||
|
|
@ -40,7 +40,7 @@ class CorpseControl(player: Player) extends Actor with ContainableBehavior {
|
||||||
val zone = obj.Zone
|
val zone = obj.Zone
|
||||||
val events = zone.AvatarEvents
|
val events = zone.AvatarEvents
|
||||||
item.Faction = PlanetSideEmpire.NEUTRAL
|
item.Faction = PlanetSideEmpire.NEUTRAL
|
||||||
events ! AvatarServiceMessage(zone.Id, AvatarAction.ObjectDelete(Service.defaultPlayerGUID, item.GUID))
|
events ! AvatarServiceMessage(zone.id, AvatarAction.ObjectDelete(Service.defaultPlayerGUID, item.GUID))
|
||||||
}
|
}
|
||||||
|
|
||||||
def PutItemInSlotCallback(item: Equipment, slot: Int): Unit = {
|
def PutItemInSlotCallback(item: Equipment, slot: Int): Unit = {
|
||||||
|
|
@ -49,7 +49,7 @@ class CorpseControl(player: Player) extends Actor with ContainableBehavior {
|
||||||
val events = zone.AvatarEvents
|
val events = zone.AvatarEvents
|
||||||
val definition = item.Definition
|
val definition = item.Definition
|
||||||
events ! AvatarServiceMessage(
|
events ! AvatarServiceMessage(
|
||||||
zone.Id,
|
zone.id,
|
||||||
AvatarAction.SendResponse(
|
AvatarAction.SendResponse(
|
||||||
Service.defaultPlayerGUID,
|
Service.defaultPlayerGUID,
|
||||||
ObjectCreateDetailedMessage(
|
ObjectCreateDetailedMessage(
|
||||||
|
|
@ -66,7 +66,7 @@ class CorpseControl(player: Player) extends Actor with ContainableBehavior {
|
||||||
val obj = ContainerObject
|
val obj = ContainerObject
|
||||||
val zone = obj.Zone
|
val zone = obj.Zone
|
||||||
zone.AvatarEvents ! AvatarServiceMessage(
|
zone.AvatarEvents ! AvatarServiceMessage(
|
||||||
zone.Id,
|
zone.id,
|
||||||
AvatarAction.SendResponse(
|
AvatarAction.SendResponse(
|
||||||
Service.defaultPlayerGUID,
|
Service.defaultPlayerGUID,
|
||||||
ObjectDetachMessage(obj.GUID, item.GUID, Vector3.Zero, 0f)
|
ObjectDetachMessage(obj.GUID, item.GUID, Vector3.Zero, 0f)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,66 @@
|
||||||
|
package net.psforever.objects.avatar
|
||||||
|
|
||||||
|
import enumeratum.values.{IntEnum, IntEnumEntry}
|
||||||
|
import scodec.{Attempt, Codec}
|
||||||
|
import scodec.codecs.uint
|
||||||
|
|
||||||
|
/** Avatar cosmetic options */
|
||||||
|
sealed abstract class Cosmetic(val value: Int) extends IntEnumEntry
|
||||||
|
|
||||||
|
case object Cosmetic extends IntEnum[Cosmetic] {
|
||||||
|
|
||||||
|
case object BrimmedCap extends Cosmetic(value = 1)
|
||||||
|
|
||||||
|
case object Earpiece extends Cosmetic(value = 2)
|
||||||
|
|
||||||
|
case object Sunglasses extends Cosmetic(value = 4)
|
||||||
|
|
||||||
|
case object Beret extends Cosmetic(value = 8)
|
||||||
|
|
||||||
|
case object NoHelmet extends Cosmetic(value = 16)
|
||||||
|
|
||||||
|
val values: IndexedSeq[Cosmetic] = findValues
|
||||||
|
|
||||||
|
/** Get enum values from ObjectCreateMessage value */
|
||||||
|
def valuesFromObjectCreateValue(value: Int): Set[Cosmetic] = {
|
||||||
|
values.filter(c => (value & c.value) == c.value).toSet
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Serialize enum values to ObjectCreateMessage value */
|
||||||
|
def valuesToObjectCreateValue(values: Set[Cosmetic]): Int = {
|
||||||
|
values.foldLeft(0)(_ + _.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get enum values from AttributeMessage value
|
||||||
|
* Attribute and object create messages use different indexes and the NoHelmet value becomes a YesHelmet value
|
||||||
|
*/
|
||||||
|
def valuesFromAttributeValue(value: Long): Set[Cosmetic] = {
|
||||||
|
var values = Set[Cosmetic]()
|
||||||
|
if (((value >> 4L) & 1L) == 1L) values += Cosmetic.Beret
|
||||||
|
if (((value >> 3L) & 1L) == 1L) values += Cosmetic.Earpiece
|
||||||
|
if (((value >> 2L) & 1L) == 1L) values += Cosmetic.Sunglasses
|
||||||
|
if (((value >> 1L) & 1L) == 1L) values += Cosmetic.BrimmedCap
|
||||||
|
if (((value >> 0L) & 1L) == 0L) values += Cosmetic.NoHelmet
|
||||||
|
values
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Serialize enum values to AttributeMessage value
|
||||||
|
* Attribute and object create messages use different indexes and the NoHelmet value becomes a YesHelmet value
|
||||||
|
*/
|
||||||
|
def valuesToAttributeValue(values: Set[Cosmetic]): Long = {
|
||||||
|
values.foldLeft(1) {
|
||||||
|
case (sum, NoHelmet) => sum - 1
|
||||||
|
case (sum, BrimmedCap) => sum + 2
|
||||||
|
case (sum, Sunglasses) => sum + 4
|
||||||
|
case (sum, Earpiece) => sum + 8
|
||||||
|
case (sum, Beret) => sum + 16
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Codec for object create messages */
|
||||||
|
implicit val codec: Codec[Set[Cosmetic]] = uint(5).exmap(
|
||||||
|
value => Attempt.Successful(Cosmetic.valuesFromObjectCreateValue(value)),
|
||||||
|
cosmetics => Attempt.Successful(Cosmetic.valuesToObjectCreateValue(cosmetics))
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -3,8 +3,7 @@ package net.psforever.objects.avatar
|
||||||
|
|
||||||
import net.psforever.objects.PlanetSideGameObject
|
import net.psforever.objects.PlanetSideGameObject
|
||||||
import net.psforever.objects.ce.{Deployable, DeployableCategory, DeployedItem}
|
import net.psforever.objects.ce.{Deployable, DeployableCategory, DeployedItem}
|
||||||
import net.psforever.types.{CertificationType, PlanetSideGUID}
|
import net.psforever.types.PlanetSideGUID
|
||||||
|
|
||||||
import scala.collection.mutable
|
import scala.collection.mutable
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -56,11 +55,12 @@ class DeployableToolbox {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set up the initial deployable counts by providing certification values to be used in category and unit selection.
|
* Set up the initial deployable counts by providing certification values to be used in category and unit selection.
|
||||||
|
*
|
||||||
* @param certifications a group of certifications for the initial values
|
* @param certifications a group of certifications for the initial values
|
||||||
* @return `true`, if this is the first time and actual "initialization" is performed;
|
* @return `true`, if this is the first time and actual "initialization" is performed;
|
||||||
* `false`, otherwise
|
* `false`, otherwise
|
||||||
*/
|
*/
|
||||||
def Initialize(certifications: Set[CertificationType.Value]): Boolean = {
|
def Initialize(certifications: Set[Certification]): Boolean = {
|
||||||
if (!initialized) {
|
if (!initialized) {
|
||||||
DeployableToolbox.Initialize(deployableCounts, categoryCounts, certifications)
|
DeployableToolbox.Initialize(deployableCounts, categoryCounts, certifications)
|
||||||
initialized = true
|
initialized = true
|
||||||
|
|
@ -79,8 +79,8 @@ class DeployableToolbox {
|
||||||
* the new certification should already have been added to this group
|
* the new certification should already have been added to this group
|
||||||
*/
|
*/
|
||||||
def AddToDeployableQuantities(
|
def AddToDeployableQuantities(
|
||||||
certification: CertificationType.Value,
|
certification: Certification,
|
||||||
certificationSet: Set[CertificationType.Value]
|
certificationSet: Set[Certification]
|
||||||
): Unit = {
|
): Unit = {
|
||||||
initialized = true
|
initialized = true
|
||||||
DeployableToolbox.AddToDeployableQuantities(deployableCounts, categoryCounts, certification, certificationSet)
|
DeployableToolbox.AddToDeployableQuantities(deployableCounts, categoryCounts, certification, certificationSet)
|
||||||
|
|
@ -96,8 +96,8 @@ class DeployableToolbox {
|
||||||
* the new certification should already have been excluded from this group
|
* the new certification should already have been excluded from this group
|
||||||
*/
|
*/
|
||||||
def RemoveFromDeployableQuantities(
|
def RemoveFromDeployableQuantities(
|
||||||
certification: CertificationType.Value,
|
certification: Certification,
|
||||||
certificationSet: Set[CertificationType.Value]
|
certificationSet: Set[Certification]
|
||||||
): Unit = {
|
): Unit = {
|
||||||
initialized = true
|
initialized = true
|
||||||
DeployableToolbox.RemoveFromDeployablesQuantities(deployableCounts, categoryCounts, certification, certificationSet)
|
DeployableToolbox.RemoveFromDeployablesQuantities(deployableCounts, categoryCounts, certification, certificationSet)
|
||||||
|
|
@ -137,8 +137,8 @@ class DeployableToolbox {
|
||||||
* `false`, otherwise
|
* `false`, otherwise
|
||||||
*/
|
*/
|
||||||
def Available(obj: DeployableToolbox.AcceptableDeployable): Boolean = {
|
def Available(obj: DeployableToolbox.AcceptableDeployable): Boolean = {
|
||||||
deployableCounts(DeployableToolbox.UnifiedType(obj.Definition.Item)).Available &&
|
deployableCounts(DeployableToolbox.UnifiedType(obj.Definition.Item)).Available() &&
|
||||||
categoryCounts(obj.Definition.DeployCategory).Available
|
categoryCounts(obj.Definition.DeployCategory).Available()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -331,8 +331,8 @@ class DeployableToolbox {
|
||||||
|
|
||||||
def UpdateUI(): List[(Int, Int, Int, Int)] = DeployedItem.values flatMap UpdateUIElement toList
|
def UpdateUI(): List[(Int, Int, Int, Int)] = DeployedItem.values flatMap UpdateUIElement toList
|
||||||
|
|
||||||
def UpdateUI(entry: CertificationType.Value): List[(Int, Int, Int, Int)] = {
|
def UpdateUI(entry: Certification): List[(Int, Int, Int, Int)] = {
|
||||||
import CertificationType._
|
import Certification._
|
||||||
entry match {
|
entry match {
|
||||||
case AdvancedHacking =>
|
case AdvancedHacking =>
|
||||||
UpdateUIElement(DeployedItem.sensor_shield)
|
UpdateUIElement(DeployedItem.sensor_shield)
|
||||||
|
|
@ -371,7 +371,7 @@ class DeployableToolbox {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def UpdateUI(certifications: List[CertificationType.Value]): List[(Int, Int, Int, Int)] = {
|
def UpdateUI(certifications: List[Certification]): List[(Int, Int, Int, Int)] = {
|
||||||
certifications flatMap UpdateUI
|
certifications flatMap UpdateUI
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -404,7 +404,7 @@ class DeployableToolbox {
|
||||||
deployableLists(category).clear()
|
deployableLists(category).clear()
|
||||||
categoryCounts(category).Current = 0
|
categoryCounts(category).Current = 0
|
||||||
(Deployable.Category.Includes(category) map DeployableToolbox.UnifiedType toSet)
|
(Deployable.Category.Includes(category) map DeployableToolbox.UnifiedType toSet)
|
||||||
.foreach({ item: DeployedItem.Value => deployableCounts(item).Current = 0 })
|
.foreach({ item: DeployedItem.Value => deployableCounts(item).Current = 0 })
|
||||||
out
|
out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -481,9 +481,9 @@ object DeployableToolbox {
|
||||||
private def Initialize(
|
private def Initialize(
|
||||||
counts: Map[DeployedItem.Value, DeployableToolbox.Bin],
|
counts: Map[DeployedItem.Value, DeployableToolbox.Bin],
|
||||||
categories: Map[DeployableCategory.Value, DeployableToolbox.Bin],
|
categories: Map[DeployableCategory.Value, DeployableToolbox.Bin],
|
||||||
certifications: Set[CertificationType.Value]
|
certifications: Set[Certification]
|
||||||
): Unit = {
|
): Unit = {
|
||||||
import CertificationType._
|
import Certification._
|
||||||
if (certifications.contains(AdvancedEngineering)) {
|
if (certifications.contains(AdvancedEngineering)) {
|
||||||
counts(DeployedItem.boomer).Max = 25
|
counts(DeployedItem.boomer).Max = 25
|
||||||
counts(DeployedItem.he_mine).Max = 25
|
counts(DeployedItem.he_mine).Max = 25
|
||||||
|
|
@ -548,7 +548,7 @@ object DeployableToolbox {
|
||||||
counts(DeployedItem.sensor_shield).Max = 20
|
counts(DeployedItem.sensor_shield).Max = 20
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (certifications.contains(CertificationType.GroundSupport)) {
|
if (certifications.contains(Certification.GroundSupport)) {
|
||||||
counts(DeployedItem.router_telepad_deployable).Max = 1024
|
counts(DeployedItem.router_telepad_deployable).Max = 1024
|
||||||
categories(DeployableCategory.Telepads).Max = 1024
|
categories(DeployableCategory.Telepads).Max = 1024
|
||||||
}
|
}
|
||||||
|
|
@ -564,10 +564,10 @@ object DeployableToolbox {
|
||||||
def AddToDeployableQuantities(
|
def AddToDeployableQuantities(
|
||||||
counts: Map[DeployedItem.Value, DeployableToolbox.Bin],
|
counts: Map[DeployedItem.Value, DeployableToolbox.Bin],
|
||||||
categories: Map[DeployableCategory.Value, DeployableToolbox.Bin],
|
categories: Map[DeployableCategory.Value, DeployableToolbox.Bin],
|
||||||
certification: CertificationType.Value,
|
certification: Certification,
|
||||||
certificationSet: Set[CertificationType.Value]
|
certificationSet: Set[Certification]
|
||||||
): Unit = {
|
): Unit = {
|
||||||
import CertificationType._
|
import Certification._
|
||||||
if (certificationSet contains certification) {
|
if (certificationSet contains certification) {
|
||||||
certification match {
|
certification match {
|
||||||
case AdvancedHacking =>
|
case AdvancedHacking =>
|
||||||
|
|
@ -649,10 +649,10 @@ object DeployableToolbox {
|
||||||
def RemoveFromDeployablesQuantities(
|
def RemoveFromDeployablesQuantities(
|
||||||
counts: Map[DeployedItem.Value, DeployableToolbox.Bin],
|
counts: Map[DeployedItem.Value, DeployableToolbox.Bin],
|
||||||
categories: Map[DeployableCategory.Value, DeployableToolbox.Bin],
|
categories: Map[DeployableCategory.Value, DeployableToolbox.Bin],
|
||||||
certification: CertificationType.Value,
|
certification: Certification,
|
||||||
certificationSet: Set[CertificationType.Value]
|
certificationSet: Set[Certification]
|
||||||
): Unit = {
|
): Unit = {
|
||||||
import CertificationType._
|
import Certification._
|
||||||
if (!certificationSet.contains(certification)) {
|
if (!certificationSet.contains(certification)) {
|
||||||
certification match {
|
certification match {
|
||||||
case AdvancedHacking =>
|
case AdvancedHacking =>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
package net.psforever.objects.avatar
|
||||||
|
|
||||||
|
import net.psforever.objects.definition.ImplantDefinition
|
||||||
|
import net.psforever.packet.game.objectcreate.ImplantEntry
|
||||||
|
|
||||||
|
case class Implant(
|
||||||
|
definition: ImplantDefinition,
|
||||||
|
active: Boolean = false,
|
||||||
|
initialized: Boolean = false
|
||||||
|
//initializationTime: FiniteDuration
|
||||||
|
) {
|
||||||
|
def toEntry: ImplantEntry = {
|
||||||
|
// TODO initialization time?
|
||||||
|
new ImplantEntry(definition.implantType, None, active)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
// Copyright (c) 2020 PSForever
|
// Copyright (c) 2020 PSForever
|
||||||
package net.psforever.objects.avatar
|
package net.psforever.objects.avatar
|
||||||
|
|
||||||
import akka.actor.{Actor, ActorRef, Cancellable, Props}
|
import akka.actor.{Actor, ActorRef, Props}
|
||||||
|
import net.psforever.actors.session.AvatarActor
|
||||||
import net.psforever.objects.{Player, _}
|
import net.psforever.objects.{Player, _}
|
||||||
import net.psforever.objects.ballistics.{PlayerSource, ResolvedProjectile}
|
import net.psforever.objects.ballistics.{PlayerSource, ResolvedProjectile}
|
||||||
import net.psforever.objects.equipment._
|
import net.psforever.objects.equipment._
|
||||||
|
|
@ -22,32 +23,26 @@ import net.psforever.types._
|
||||||
import services.{RemoverActor, Service}
|
import services.{RemoverActor, Service}
|
||||||
import services.avatar.{AvatarAction, AvatarServiceMessage}
|
import services.avatar.{AvatarAction, AvatarServiceMessage}
|
||||||
import services.local.{LocalAction, LocalServiceMessage}
|
import services.local.{LocalAction, LocalServiceMessage}
|
||||||
|
import akka.actor.typed
|
||||||
import scala.concurrent.duration._
|
import scala.concurrent.duration._
|
||||||
import scala.collection.mutable
|
|
||||||
import scala.concurrent.ExecutionContext.Implicits.global
|
|
||||||
|
|
||||||
class PlayerControl(player: Player) extends Actor with JammableBehavior with Damageable with ContainableBehavior {
|
class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Command])
|
||||||
def JammableObject = player
|
extends Actor
|
||||||
|
with JammableBehavior
|
||||||
|
with Damageable
|
||||||
|
with ContainableBehavior {
|
||||||
|
def JammableObject = player
|
||||||
|
|
||||||
def DamageableObject = player
|
def DamageableObject = player
|
||||||
def ContainerObject = player
|
|
||||||
|
def ContainerObject = player
|
||||||
|
|
||||||
private[this] val log = org.log4s.getLogger(player.Name)
|
private[this] val log = org.log4s.getLogger(player.Name)
|
||||||
private[this] val damageLog = org.log4s.getLogger(Damageable.LogChannel)
|
private[this] val damageLog = org.log4s.getLogger(Damageable.LogChannel)
|
||||||
|
|
||||||
/** Stamina will be used. Stamina will be restored. */
|
|
||||||
var staminaRegen: Cancellable = Default.Cancellable
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A collection of timers indexed for the implant in each slot.
|
|
||||||
* Before an implant is ready, it serves as the initialization timer.
|
|
||||||
* After being initialized, it is used as the stamina drain interval when the implant is active.
|
|
||||||
*/
|
|
||||||
val implantSlotTimers = mutable.HashMap(0 -> Default.Cancellable, 1 -> Default.Cancellable, 2 -> Default.Cancellable)
|
|
||||||
|
|
||||||
/** control agency for the player's locker container (dedicated inventory slot #5) */
|
/** control agency for the player's locker container (dedicated inventory slot #5) */
|
||||||
val lockerControlAgent: ActorRef = {
|
val lockerControlAgent: ActorRef = {
|
||||||
val locker = player.Locker
|
val locker = player.avatar.locker
|
||||||
locker.Zone = player.Zone
|
locker.Zone = player.Zone
|
||||||
locker.Actor = context.actorOf(
|
locker.Actor = context.actorOf(
|
||||||
Props(classOf[LockerContainerControl], locker, player.Name),
|
Props(classOf[LockerContainerControl], locker, player.Name),
|
||||||
|
|
@ -57,9 +52,7 @@ class PlayerControl(player: Player) extends Actor with JammableBehavior with Dam
|
||||||
|
|
||||||
override def postStop(): Unit = {
|
override def postStop(): Unit = {
|
||||||
lockerControlAgent ! akka.actor.PoisonPill
|
lockerControlAgent ! akka.actor.PoisonPill
|
||||||
player.Locker.Actor = Default.Actor
|
player.avatar.locker.Actor = Default.Actor
|
||||||
staminaRegen.cancel
|
|
||||||
implantSlotTimers.values.foreach { _.cancel }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def receive: Receive =
|
def receive: Receive =
|
||||||
|
|
@ -67,47 +60,6 @@ class PlayerControl(player: Player) extends Actor with JammableBehavior with Dam
|
||||||
.orElse(takesDamage)
|
.orElse(takesDamage)
|
||||||
.orElse(containerBehavior)
|
.orElse(containerBehavior)
|
||||||
.orElse {
|
.orElse {
|
||||||
case Player.ImplantActivation(slot: Int, status: Int) =>
|
|
||||||
ImplantActivation(slot, status)
|
|
||||||
|
|
||||||
case Player.UninitializeImplant(slot: Int) =>
|
|
||||||
UninitializeImplant(slot)
|
|
||||||
|
|
||||||
case Player.ImplantInitializationStart(slot: Int) =>
|
|
||||||
ImplantInitializationStart(slot)
|
|
||||||
|
|
||||||
case Player.ImplantInitializationComplete(slot: Int) =>
|
|
||||||
ImplantInitializationComplete(slot)
|
|
||||||
|
|
||||||
case Player.StaminaRegen() =>
|
|
||||||
if (staminaRegen == Default.Cancellable) {
|
|
||||||
staminaRegen.cancel
|
|
||||||
staminaRegen =
|
|
||||||
context.system.scheduler.scheduleOnce(delay = 500 milliseconds, self, PlayerControl.StaminaRegen())
|
|
||||||
}
|
|
||||||
|
|
||||||
case PlayerControl.StaminaRegen() =>
|
|
||||||
staminaRegen.cancel
|
|
||||||
if (player.isAlive) {
|
|
||||||
if (player.skipStaminaRegenForTurns > 0) {
|
|
||||||
// Do not renew stamina for a while
|
|
||||||
player.skipStaminaRegenForTurns -= 1
|
|
||||||
} else if (
|
|
||||||
(player.VehicleSeated.nonEmpty || !player.isMoving && !player.Jumping) && player.Stamina < player.MaxStamina
|
|
||||||
) {
|
|
||||||
// Regen stamina roughly every 500ms
|
|
||||||
StaminaChanged(changeInStamina = 1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
staminaRegen =
|
|
||||||
context.system.scheduler.scheduleOnce(delay = 500 milliseconds, self, PlayerControl.StaminaRegen())
|
|
||||||
|
|
||||||
case Player.StaminaChanged(Some(changeInStamina)) =>
|
|
||||||
StaminaChanged(changeInStamina)
|
|
||||||
|
|
||||||
case Player.StaminaChanged(None) =>
|
|
||||||
UpdateStamina()
|
|
||||||
|
|
||||||
case Player.Die() =>
|
case Player.Die() =>
|
||||||
if (player.isAlive) {
|
if (player.isAlive) {
|
||||||
DestructionAwareness(player, None)
|
DestructionAwareness(player, None)
|
||||||
|
|
@ -138,7 +90,7 @@ class PlayerControl(player: Player) extends Actor with JammableBehavior with Dam
|
||||||
InventoryStateMessage(item.AmmoSlot.Box.GUID, item.GUID, magazine.toLong)
|
InventoryStateMessage(item.AmmoSlot.Box.GUID, item.GUID, magazine.toLong)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
events ! AvatarServiceMessage(zone.Id, AvatarAction.PlanetsideAttributeToAll(guid, 0, newHealth))
|
events ! AvatarServiceMessage(zone.id, AvatarAction.PlanetsideAttributeToAll(guid, 0, newHealth))
|
||||||
player.History(
|
player.History(
|
||||||
HealFromEquipment(
|
HealFromEquipment(
|
||||||
PlayerSource(player),
|
PlayerSource(player),
|
||||||
|
|
@ -171,7 +123,7 @@ class PlayerControl(player: Player) extends Actor with JammableBehavior with Dam
|
||||||
!player.isAlive && !player.isBackpack &&
|
!player.isAlive && !player.isBackpack &&
|
||||||
item.Magazine >= 25
|
item.Magazine >= 25
|
||||||
) {
|
) {
|
||||||
sender ! CommonMessages.Progress(
|
sender() ! CommonMessages.Progress(
|
||||||
4,
|
4,
|
||||||
Players.FinishRevivingPlayer(player, user.Name, item),
|
Players.FinishRevivingPlayer(player, user.Name, item),
|
||||||
Players.RevivingTickAction(player, user, item)
|
Players.RevivingTickAction(player, user, item)
|
||||||
|
|
@ -202,7 +154,7 @@ class PlayerControl(player: Player) extends Actor with JammableBehavior with Dam
|
||||||
InventoryStateMessage(item.AmmoSlot.Box.GUID, item.GUID, magazine.toLong)
|
InventoryStateMessage(item.AmmoSlot.Box.GUID, item.GUID, magazine.toLong)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
events ! AvatarServiceMessage(zone.Id, AvatarAction.PlanetsideAttributeToAll(guid, 4, player.Armor))
|
events ! AvatarServiceMessage(zone.id, AvatarAction.PlanetsideAttributeToAll(guid, 4, player.Armor))
|
||||||
player.History(
|
player.History(
|
||||||
RepairFromEquipment(
|
RepairFromEquipment(
|
||||||
PlayerSource(player),
|
PlayerSource(player),
|
||||||
|
|
@ -232,26 +184,29 @@ class PlayerControl(player: Player) extends Actor with JammableBehavior with Dam
|
||||||
case Terminal.TerminalMessage(_, msg, order) =>
|
case Terminal.TerminalMessage(_, msg, order) =>
|
||||||
order match {
|
order match {
|
||||||
case Terminal.BuyExosuit(exosuit, subtype) =>
|
case Terminal.BuyExosuit(exosuit, subtype) =>
|
||||||
val time = System.currentTimeMillis
|
|
||||||
var toDelete: List[InventoryItem] = Nil
|
var toDelete: List[InventoryItem] = Nil
|
||||||
val originalSuit = player.ExoSuit
|
val originalSuit = player.ExoSuit
|
||||||
val originalSubtype = Loadout.DetermineSubtype(player)
|
val originalSubtype = Loadout.DetermineSubtype(player)
|
||||||
val requestToChangeArmor = originalSuit != exosuit || originalSubtype != subtype
|
val requestToChangeArmor = originalSuit != exosuit || originalSubtype != subtype
|
||||||
val allowedToChangeArmor = Players.CertificationToUseExoSuit(player, exosuit, subtype) &&
|
val allowedToChangeArmor = Players.CertificationToUseExoSuit(player, exosuit, subtype) &&
|
||||||
(if (exosuit == ExoSuitType.MAX) {
|
(if (exosuit == ExoSuitType.MAX) {
|
||||||
if (time - player.GetLastUsedTime(exosuit, subtype) < 300000L) {
|
val definition = player.avatar.faction match {
|
||||||
false
|
case PlanetSideEmpire.NC => GlobalDefinitions.NCMAX
|
||||||
} else {
|
case PlanetSideEmpire.TR => GlobalDefinitions.TRMAX
|
||||||
player.SetLastUsedTime(exosuit, subtype, time)
|
case PlanetSideEmpire.VS => GlobalDefinitions.VSMAX
|
||||||
true
|
}
|
||||||
|
player.avatar.purchaseCooldown(definition) match {
|
||||||
|
case Some(_) =>
|
||||||
|
false
|
||||||
|
case None =>
|
||||||
|
avatarActor ! AvatarActor.UpdatePurchaseTime(definition)
|
||||||
|
true
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
player.SetLastUsedTime(exosuit, subtype, time)
|
|
||||||
true
|
true
|
||||||
})
|
})
|
||||||
val result = if (requestToChangeArmor && allowedToChangeArmor) {
|
val result = if (requestToChangeArmor && allowedToChangeArmor) {
|
||||||
log.info(s"${player.Name} wants to change to a different exo-suit - $exosuit")
|
log.info(s"${player.Name} wants to change to a different exo-suit - $exosuit")
|
||||||
player.SetLastUsedTime(exosuit, subtype, System.currentTimeMillis())
|
|
||||||
val beforeHolsters = Players.clearHolsters(player.Holsters().iterator)
|
val beforeHolsters = Players.clearHolsters(player.Holsters().iterator)
|
||||||
val beforeInventory = player.Inventory.Clear()
|
val beforeInventory = player.Inventory.Clear()
|
||||||
//change suit
|
//change suit
|
||||||
|
|
@ -314,18 +269,9 @@ class PlayerControl(player: Player) extends Actor with JammableBehavior with Dam
|
||||||
player.Inventory.InsertQuickly(elem.start, elem.obj)
|
player.Inventory.InsertQuickly(elem.start, elem.obj)
|
||||||
}
|
}
|
||||||
//deactivate non-passive implants
|
//deactivate non-passive implants
|
||||||
implantSlotTimers.keys.foreach { index =>
|
avatarActor ! AvatarActor.DeactivateActiveImplants()
|
||||||
val implantSlot = player.ImplantSlot(index)
|
|
||||||
if (
|
|
||||||
implantSlot.Installed.nonEmpty && implantSlot.Active && (implantSlot.Charge(
|
|
||||||
originalSuit
|
|
||||||
) > 0 || implantSlot.Charge(exosuit) > 0)
|
|
||||||
) {
|
|
||||||
ImplantActivation(index, status = 0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
player.Zone.AvatarEvents ! AvatarServiceMessage(
|
player.Zone.AvatarEvents ! AvatarServiceMessage(
|
||||||
player.Zone.Id,
|
player.Zone.id,
|
||||||
AvatarAction.ChangeExosuit(
|
AvatarAction.ChangeExosuit(
|
||||||
player.GUID,
|
player.GUID,
|
||||||
exosuit,
|
exosuit,
|
||||||
|
|
@ -375,19 +321,22 @@ class PlayerControl(player: Player) extends Actor with JammableBehavior with Dam
|
||||||
}) ++ dropHolsters ++ dropInventory
|
}) ++ dropHolsters ++ dropInventory
|
||||||
//a loadout with a prohibited exo-suit type will result in the fallback exo-suit type
|
//a loadout with a prohibited exo-suit type will result in the fallback exo-suit type
|
||||||
//imposed 5min delay on mechanized exo-suit switches
|
//imposed 5min delay on mechanized exo-suit switches
|
||||||
val time = System.currentTimeMillis()
|
|
||||||
val (nextSuit, nextSubtype) =
|
val (nextSuit, nextSubtype) =
|
||||||
if (
|
if (
|
||||||
Players.CertificationToUseExoSuit(player, exosuit, subtype) &&
|
Players.CertificationToUseExoSuit(player, exosuit, subtype) &&
|
||||||
(if (exosuit == ExoSuitType.MAX) {
|
(if (exosuit == ExoSuitType.MAX) {
|
||||||
if (time - player.GetLastUsedTime(exosuit, subtype) < 300000L) {
|
val definition = player.avatar.faction match {
|
||||||
false
|
case PlanetSideEmpire.NC => GlobalDefinitions.NCMAX
|
||||||
} else {
|
case PlanetSideEmpire.TR => GlobalDefinitions.TRMAX
|
||||||
player.SetLastUsedTime(exosuit, subtype, time)
|
case PlanetSideEmpire.VS => GlobalDefinitions.VSMAX
|
||||||
true
|
}
|
||||||
|
player.avatar.purchaseCooldown(definition) match {
|
||||||
|
case Some(_) => false
|
||||||
|
case None =>
|
||||||
|
avatarActor ! AvatarActor.UpdatePurchaseTime(definition)
|
||||||
|
true
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
player.SetLastUsedTime(exosuit, subtype, time)
|
|
||||||
true
|
true
|
||||||
})
|
})
|
||||||
) {
|
) {
|
||||||
|
|
@ -396,7 +345,6 @@ class PlayerControl(player: Player) extends Actor with JammableBehavior with Dam
|
||||||
log.warn(
|
log.warn(
|
||||||
s"no longer has permission to wear the exo-suit type $exosuit; will wear $fallbackSuit instead"
|
s"no longer has permission to wear the exo-suit type $exosuit; will wear $fallbackSuit instead"
|
||||||
)
|
)
|
||||||
player.SetLastUsedTime(fallbackSuit, fallbackSubtype, time)
|
|
||||||
(fallbackSuit, fallbackSubtype)
|
(fallbackSuit, fallbackSubtype)
|
||||||
}
|
}
|
||||||
//sanitize (incoming) inventory
|
//sanitize (incoming) inventory
|
||||||
|
|
@ -458,18 +406,9 @@ class PlayerControl(player: Player) extends Actor with JammableBehavior with Dam
|
||||||
(afterHolsters ++ afterInventory).foreach { entry => entry.obj.Faction = player.Faction }
|
(afterHolsters ++ afterInventory).foreach { entry => entry.obj.Faction = player.Faction }
|
||||||
toDeleteOrDrop.foreach { entry => entry.obj.Faction = PlanetSideEmpire.NEUTRAL }
|
toDeleteOrDrop.foreach { entry => entry.obj.Faction = PlanetSideEmpire.NEUTRAL }
|
||||||
//deactivate non-passive implants
|
//deactivate non-passive implants
|
||||||
implantSlotTimers.keys.foreach { index =>
|
avatarActor ! AvatarActor.DeactivateActiveImplants()
|
||||||
val implantSlot = player.ImplantSlot(index)
|
|
||||||
if (
|
|
||||||
implantSlot.Installed.nonEmpty && implantSlot.Active && (implantSlot.Charge(
|
|
||||||
originalSuit
|
|
||||||
) > 0 || implantSlot.Charge(nextSuit) > 0)
|
|
||||||
) {
|
|
||||||
ImplantActivation(index, status = 0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
player.Zone.AvatarEvents ! AvatarServiceMessage(
|
player.Zone.AvatarEvents ! AvatarServiceMessage(
|
||||||
player.Zone.Id,
|
player.Zone.id,
|
||||||
AvatarAction.ChangeLoadout(
|
AvatarAction.ChangeLoadout(
|
||||||
player.GUID,
|
player.GUID,
|
||||||
nextSuit,
|
nextSuit,
|
||||||
|
|
@ -487,103 +426,7 @@ class PlayerControl(player: Player) extends Actor with JammableBehavior with Dam
|
||||||
player.Name,
|
player.Name,
|
||||||
AvatarAction.TerminalOrderResult(msg.terminal_guid, msg.transaction_type, true)
|
AvatarAction.TerminalOrderResult(msg.terminal_guid, msg.transaction_type, true)
|
||||||
)
|
)
|
||||||
|
case _ => assert(false, msg.toString)
|
||||||
case Terminal.LearnImplant(implant) =>
|
|
||||||
val zone = player.Zone
|
|
||||||
val events = zone.AvatarEvents
|
|
||||||
val playerChannel = player.Name
|
|
||||||
val terminal_guid = msg.terminal_guid
|
|
||||||
val implant_type = implant.Type
|
|
||||||
val message = s"wants to learn $implant_type"
|
|
||||||
val (interface, slotNumber) = player.VehicleSeated match {
|
|
||||||
case Some(mech_guid) =>
|
|
||||||
(
|
|
||||||
zone.map.TerminalToInterface.get(mech_guid.guid),
|
|
||||||
if (!player.Implants.exists({ case (implantType, _, _) => implantType == implant_type })) {
|
|
||||||
//no duplicates
|
|
||||||
player.InstallImplant(implant)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
)
|
|
||||||
case _ =>
|
|
||||||
(None, None)
|
|
||||||
}
|
|
||||||
val result = if (interface.contains(terminal_guid.guid) && slotNumber.isDefined) {
|
|
||||||
val slot = slotNumber.get
|
|
||||||
log.info(s"$message - put in slot $slot")
|
|
||||||
events ! AvatarServiceMessage(
|
|
||||||
playerChannel,
|
|
||||||
AvatarAction.SendResponse(
|
|
||||||
Service.defaultPlayerGUID,
|
|
||||||
AvatarImplantMessage(player.GUID, ImplantAction.Add, slot, implant_type.id)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
ImplantInitializationStart(slot)
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
if (interface.isEmpty) {
|
|
||||||
log.warn(s"$message - not interacting with a terminal")
|
|
||||||
} else if (!interface.contains(terminal_guid.guid)) {
|
|
||||||
log.warn(s"$message - interacting with the wrong terminal, ${interface.get}")
|
|
||||||
} else if (slotNumber.isEmpty) {
|
|
||||||
log.warn(s"$message - already knows that implant")
|
|
||||||
} else {
|
|
||||||
log.warn(s"$message - forgot to sit at a terminal")
|
|
||||||
}
|
|
||||||
false
|
|
||||||
}
|
|
||||||
events ! AvatarServiceMessage(
|
|
||||||
playerChannel,
|
|
||||||
AvatarAction.TerminalOrderResult(terminal_guid, msg.transaction_type, result)
|
|
||||||
)
|
|
||||||
|
|
||||||
case Terminal.SellImplant(implant) =>
|
|
||||||
val zone = player.Zone
|
|
||||||
val events = zone.AvatarEvents
|
|
||||||
val playerChannel = player.Name
|
|
||||||
val terminal_guid = msg.terminal_guid
|
|
||||||
val implant_type = implant.Type
|
|
||||||
val (interface, slotNumber) = player.VehicleSeated match {
|
|
||||||
case Some(mech_guid) =>
|
|
||||||
(
|
|
||||||
zone.map.TerminalToInterface.get(mech_guid.guid),
|
|
||||||
player.UninstallImplant(implant_type)
|
|
||||||
)
|
|
||||||
case None =>
|
|
||||||
(None, None)
|
|
||||||
}
|
|
||||||
val result = if (interface.contains(terminal_guid.guid) && slotNumber.isDefined) {
|
|
||||||
val slot = slotNumber.get
|
|
||||||
log.info(s"is uninstalling $implant_type - take from slot $slot")
|
|
||||||
UninitializeImplant(slot)
|
|
||||||
events ! AvatarServiceMessage(
|
|
||||||
playerChannel,
|
|
||||||
AvatarAction.SendResponse(
|
|
||||||
Service.defaultPlayerGUID,
|
|
||||||
AvatarImplantMessage(player.GUID, ImplantAction.Remove, slot, 0)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
val message = s"${player.Name} can not sell $implant_type"
|
|
||||||
if (interface.isEmpty) {
|
|
||||||
log.warn(s"$message - not interacting with a terminal")
|
|
||||||
} else if (!interface.contains(terminal_guid.guid)) {
|
|
||||||
log.warn(s"$message - interacting with the wrong terminal, ${interface.get}")
|
|
||||||
} else if (slotNumber.isEmpty) {
|
|
||||||
log.warn(s"$message - does not know that implant")
|
|
||||||
} else {
|
|
||||||
log.warn(s"$message - forgot to sit at a terminal")
|
|
||||||
}
|
|
||||||
false
|
|
||||||
}
|
|
||||||
events ! AvatarServiceMessage(
|
|
||||||
playerChannel,
|
|
||||||
AvatarAction.TerminalOrderResult(terminal_guid, msg.transaction_type, result)
|
|
||||||
)
|
|
||||||
|
|
||||||
case _ => ; //terminal messages not handled here
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case Zone.Ground.ItemOnGround(item, _, _) =>
|
case Zone.Ground.ItemOnGround(item, _, _) =>
|
||||||
|
|
@ -599,10 +442,10 @@ class PlayerControl(player: Player) extends Actor with JammableBehavior with Dam
|
||||||
case (Some(boomer: BoomerDeployable), Some(avatar)) =>
|
case (Some(boomer: BoomerDeployable), Some(avatar)) =>
|
||||||
val guid = boomer.GUID
|
val guid = boomer.GUID
|
||||||
val factionChannel = boomer.Faction.toString
|
val factionChannel = boomer.Faction.toString
|
||||||
if (avatar.Deployables.Remove(boomer)) {
|
if (avatar.deployables.Remove(boomer)) {
|
||||||
boomer.Faction = PlanetSideEmpire.NEUTRAL
|
boomer.Faction = PlanetSideEmpire.NEUTRAL
|
||||||
boomer.AssignOwnership(None)
|
boomer.AssignOwnership(None)
|
||||||
avatar.Deployables.UpdateUIElement(boomer.Definition.Item).foreach {
|
avatar.deployables.UpdateUIElement(boomer.Definition.Item).foreach {
|
||||||
case (currElem, curr, maxElem, max) =>
|
case (currElem, curr, maxElem, max) =>
|
||||||
avatarEvents ! AvatarServiceMessage(
|
avatarEvents ! AvatarServiceMessage(
|
||||||
name,
|
name,
|
||||||
|
|
@ -648,12 +491,12 @@ class PlayerControl(player: Player) extends Actor with JammableBehavior with Dam
|
||||||
if (player.isAlive && !player.spectator) {
|
if (player.isAlive && !player.spectator) {
|
||||||
val originalHealth = player.Health
|
val originalHealth = player.Health
|
||||||
val originalArmor = player.Armor
|
val originalArmor = player.Armor
|
||||||
val originalStamina = player.Stamina
|
val originalStamina = player.avatar.stamina
|
||||||
val originalCapacitor = player.Capacitor.toInt
|
val originalCapacitor = player.Capacitor.toInt
|
||||||
val cause = applyDamageTo(player)
|
val cause = applyDamageTo(player)
|
||||||
val health = player.Health
|
val health = player.Health
|
||||||
val armor = player.Armor
|
val armor = player.Armor
|
||||||
val stamina = player.Stamina
|
val stamina = player.avatar.stamina
|
||||||
val capacitor = player.Capacitor.toInt
|
val capacitor = player.Capacitor.toInt
|
||||||
val damageToHealth = originalHealth - health
|
val damageToHealth = originalHealth - health
|
||||||
val damageToArmor = originalArmor - armor
|
val damageToArmor = originalArmor - armor
|
||||||
|
|
@ -682,7 +525,7 @@ class PlayerControl(player: Player) extends Actor with JammableBehavior with Dam
|
||||||
): Unit = {
|
): Unit = {
|
||||||
val targetGUID = target.GUID
|
val targetGUID = target.GUID
|
||||||
val zone = target.Zone
|
val zone = target.Zone
|
||||||
val zoneId = zone.Id
|
val zoneId = zone.id
|
||||||
val events = zone.AvatarEvents
|
val events = zone.AvatarEvents
|
||||||
val health = target.Health
|
val health = target.Health
|
||||||
if (damageToArmor > 0) {
|
if (damageToArmor > 0) {
|
||||||
|
|
@ -701,7 +544,7 @@ class PlayerControl(player: Player) extends Actor with JammableBehavior with Dam
|
||||||
events ! AvatarServiceMessage(zoneId, AvatarAction.PlanetsideAttributeToAll(targetGUID, 0, health))
|
events ! AvatarServiceMessage(zoneId, AvatarAction.PlanetsideAttributeToAll(targetGUID, 0, health))
|
||||||
}
|
}
|
||||||
if (damageToStamina > 0) {
|
if (damageToStamina > 0) {
|
||||||
UpdateStamina()
|
avatarActor ! AvatarActor.ConsumeStamina(damageToStamina)
|
||||||
}
|
}
|
||||||
//activity on map
|
//activity on map
|
||||||
zone.Activity ! Zone.HotSpot.Activity(cause.target, cause.projectile.owner, cause.hit_pos)
|
zone.Activity ! Zone.HotSpot.Activity(cause.target, cause.projectile.owner, cause.hit_pos)
|
||||||
|
|
@ -761,20 +604,13 @@ class PlayerControl(player: Player) extends Actor with JammableBehavior with Dam
|
||||||
val zone = target.Zone
|
val zone = target.Zone
|
||||||
val events = zone.AvatarEvents
|
val events = zone.AvatarEvents
|
||||||
val nameChannel = target.Name
|
val nameChannel = target.Name
|
||||||
val zoneChannel = zone.Id
|
val zoneChannel = zone.id
|
||||||
target.Die
|
target.Die
|
||||||
//unjam
|
//unjam
|
||||||
CancelJammeredSound(target)
|
CancelJammeredSound(target)
|
||||||
CancelJammeredStatus(target)
|
CancelJammeredStatus(target)
|
||||||
//implants off
|
|
||||||
target.Stamina = 0
|
|
||||||
UpdateStamina() //turn off implants / OutOfStamina
|
|
||||||
//uninitialize implants
|
//uninitialize implants
|
||||||
target.Implants.indices.foreach {
|
avatarActor ! AvatarActor.DeinitializeImplants()
|
||||||
case slot if target.Implant(slot) != ImplantType.None =>
|
|
||||||
UninitializeImplant(slot)
|
|
||||||
}
|
|
||||||
target.ResetAllImplants() //anything else specific to the backend
|
|
||||||
events ! AvatarServiceMessage(
|
events ! AvatarServiceMessage(
|
||||||
nameChannel,
|
nameChannel,
|
||||||
AvatarAction.Killed(player_guid, target.VehicleSeated)
|
AvatarAction.Killed(player_guid, target.VehicleSeated)
|
||||||
|
|
@ -873,7 +709,7 @@ class PlayerControl(player: Player) extends Actor with JammableBehavior with Dam
|
||||||
target match {
|
target match {
|
||||||
case obj: Player if !jammedSound =>
|
case obj: Player if !jammedSound =>
|
||||||
obj.Zone.AvatarEvents ! AvatarServiceMessage(
|
obj.Zone.AvatarEvents ! AvatarServiceMessage(
|
||||||
obj.Zone.Id,
|
obj.Zone.id,
|
||||||
AvatarAction.PlanetsideAttributeToAll(obj.GUID, 27, 1)
|
AvatarAction.PlanetsideAttributeToAll(obj.GUID, 27, 1)
|
||||||
)
|
)
|
||||||
super.StartJammeredSound(obj, 3000)
|
super.StartJammeredSound(obj, 3000)
|
||||||
|
|
@ -890,25 +726,13 @@ class PlayerControl(player: Player) extends Actor with JammableBehavior with Dam
|
||||||
* @param dur the duration of the timer, in milliseconds
|
* @param dur the duration of the timer, in milliseconds
|
||||||
*/
|
*/
|
||||||
override def StartJammeredStatus(target: Any, dur: Int): Unit = {
|
override def StartJammeredStatus(target: Any, dur: Int): Unit = {
|
||||||
//TODO these features
|
avatarActor ! AvatarActor.DeinitializeImplants()
|
||||||
val zone = player.Zone
|
avatarActor ! AvatarActor.SuspendStaminaRegeneration(5 seconds)
|
||||||
player.Implants.indices.foreach { slot => // Deactivate & uninitialize all implants
|
|
||||||
zone.AvatarEvents ! AvatarServiceMessage(
|
|
||||||
zone.Id,
|
|
||||||
AvatarAction.PlanetsideAttribute(player.GUID, 28, player.Implant(slot).id * 2)
|
|
||||||
) // Deactivation sound / effect
|
|
||||||
ImplantActivation(slot, status = 0)
|
|
||||||
UninitializeImplant(slot)
|
|
||||||
}
|
|
||||||
player.skipStaminaRegenForTurns = math.max(player.skipStaminaRegenForTurns, 10)
|
|
||||||
super.StartJammeredStatus(target, dur)
|
super.StartJammeredStatus(target, dur)
|
||||||
}
|
}
|
||||||
|
|
||||||
override def CancelJammeredStatus(target: Any): Unit = {
|
override def CancelJammeredStatus(target: Any): Unit = {
|
||||||
player.Implants.indices.foreach { slot => // Start reinitializing all implants
|
avatarActor ! AvatarActor.InitializeImplants(instant = true)
|
||||||
player.ImplantSlot(slot).InitializeTime = 0 //setting time to 0 will restart implant initialization (eventually)
|
|
||||||
ImplantInitializationStart(slot)
|
|
||||||
}
|
|
||||||
super.CancelJammeredStatus(target)
|
super.CancelJammeredStatus(target)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -921,7 +745,7 @@ class PlayerControl(player: Player) extends Actor with JammableBehavior with Dam
|
||||||
target match {
|
target match {
|
||||||
case obj: Player if jammedSound =>
|
case obj: Player if jammedSound =>
|
||||||
obj.Zone.AvatarEvents ! AvatarServiceMessage(
|
obj.Zone.AvatarEvents ! AvatarServiceMessage(
|
||||||
obj.Zone.Id,
|
obj.Zone.id,
|
||||||
AvatarAction.PlanetsideAttributeToAll(obj.GUID, 27, 0)
|
AvatarAction.PlanetsideAttributeToAll(obj.GUID, 27, 0)
|
||||||
)
|
)
|
||||||
super.CancelJammeredSound(obj)
|
super.CancelJammeredSound(obj)
|
||||||
|
|
@ -956,7 +780,7 @@ class PlayerControl(player: Player) extends Actor with JammableBehavior with Dam
|
||||||
val obj = ContainerObject
|
val obj = ContainerObject
|
||||||
val zone = obj.Zone
|
val zone = obj.Zone
|
||||||
val events = zone.AvatarEvents
|
val events = zone.AvatarEvents
|
||||||
val toChannel = if (obj.VisibleSlots.contains(slot)) zone.Id else player.Name
|
val toChannel = if (obj.VisibleSlots.contains(slot)) zone.id else player.Name
|
||||||
item.Faction = PlanetSideEmpire.NEUTRAL
|
item.Faction = PlanetSideEmpire.NEUTRAL
|
||||||
if (slot == obj.DrawnSlot) {
|
if (slot == obj.DrawnSlot) {
|
||||||
obj.DrawnSlot = Player.HandsDownSlot
|
obj.DrawnSlot = Player.HandsDownSlot
|
||||||
|
|
@ -986,7 +810,7 @@ class PlayerControl(player: Player) extends Actor with JammableBehavior with Dam
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
if (obj.VisibleSlots.contains(slot)) {
|
if (obj.VisibleSlots.contains(slot)) {
|
||||||
events ! AvatarServiceMessage(zone.Id, AvatarAction.EquipmentInHand(guid, guid, slot, item))
|
events ! AvatarServiceMessage(zone.id, AvatarAction.EquipmentInHand(guid, guid, slot, item))
|
||||||
}
|
}
|
||||||
//handle specific types of items
|
//handle specific types of items
|
||||||
item match {
|
item match {
|
||||||
|
|
@ -998,10 +822,10 @@ class PlayerControl(player: Player) extends Actor with JammableBehavior with Dam
|
||||||
val bguid = boomer.GUID
|
val bguid = boomer.GUID
|
||||||
val faction = player.Faction
|
val faction = player.Faction
|
||||||
val factionChannel = faction.toString
|
val factionChannel = faction.toString
|
||||||
if (avatar.Deployables.Add(boomer)) {
|
if (avatar.deployables.Add(boomer)) {
|
||||||
boomer.Faction = faction
|
boomer.Faction = faction
|
||||||
boomer.AssignOwnership(player)
|
boomer.AssignOwnership(player)
|
||||||
avatar.Deployables.UpdateUIElement(boomer.Definition.Item).foreach {
|
avatar.deployables.UpdateUIElement(boomer.Definition.Item).foreach {
|
||||||
case (currElem, curr, maxElem, max) =>
|
case (currElem, curr, maxElem, max) =>
|
||||||
events ! AvatarServiceMessage(
|
events ! AvatarServiceMessage(
|
||||||
name,
|
name,
|
||||||
|
|
@ -1045,250 +869,4 @@ class PlayerControl(player: Player) extends Actor with JammableBehavior with Dam
|
||||||
AvatarAction.SendResponse(Service.defaultPlayerGUID, ObjectDetachMessage(obj.GUID, item.GUID, Vector3.Zero, 0f))
|
AvatarAction.SendResponse(Service.defaultPlayerGUID, ObjectDetachMessage(obj.GUID, item.GUID, Vector3.Zero, 0f))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* na
|
|
||||||
* @param changeInStamina na
|
|
||||||
*/
|
|
||||||
def StaminaChanged(changeInStamina: Int): Unit = {
|
|
||||||
val beforeStamina = player.Stamina
|
|
||||||
val afterStamina = player.Stamina += changeInStamina
|
|
||||||
if (beforeStamina != afterStamina) {
|
|
||||||
UpdateStamina()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine whether the current stamina value for this player requires a greater change in player states.
|
|
||||||
* Losing all stamina and not yet being fatigued deactivates implants.
|
|
||||||
* Having stamina of 20 points or greater and having previously been fatigued
|
|
||||||
* allows implants to operate once again.
|
|
||||||
* Initialization must be restarted manually for any implant that had not previously finished initializing.
|
|
||||||
*/
|
|
||||||
def UpdateStamina(): Unit = {
|
|
||||||
val currentStamina = player.Stamina
|
|
||||||
if (currentStamina == 0 && !player.Fatigued) { // Only be fatigued once even if loses all stamina again
|
|
||||||
player.Fatigued = true
|
|
||||||
player.skipStaminaRegenForTurns = math.max(player.skipStaminaRegenForTurns, 6)
|
|
||||||
player.Implants.indices.foreach { slot => // Disable all implants
|
|
||||||
ImplantActivation(slot, status = 0)
|
|
||||||
player.Zone.AvatarEvents ! AvatarServiceMessage(
|
|
||||||
player.Name,
|
|
||||||
AvatarAction.SendResponse(
|
|
||||||
Service.defaultPlayerGUID,
|
|
||||||
AvatarImplantMessage(player.GUID, ImplantAction.OutOfStamina, slot, 1)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else if (currentStamina >= 20) {
|
|
||||||
val wasFatigued = player.Fatigued
|
|
||||||
player.Fatigued = false
|
|
||||||
if (wasFatigued) { //reactivate only if we were fatigued
|
|
||||||
player.Implants.indices.foreach { slot => // Re-enable all implants
|
|
||||||
player.Zone.AvatarEvents ! AvatarServiceMessage(
|
|
||||||
player.Name,
|
|
||||||
AvatarAction.SendResponse(
|
|
||||||
Service.defaultPlayerGUID,
|
|
||||||
AvatarImplantMessage(player.GUID, ImplantAction.OutOfStamina, slot, 0)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
if (!player.ImplantSlot(slot).Initialized) {
|
|
||||||
ImplantInitializationStart(slot)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
player.Zone.AvatarEvents ! AvatarServiceMessage(
|
|
||||||
player.Name,
|
|
||||||
AvatarAction.PlanetsideAttributeToAll(player.GUID, 2, currentStamina)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The process of starting an implant so that it can be activated is one that requires a matter of time.
|
|
||||||
* If the implant should already have been started, then just switch to the proper state.
|
|
||||||
* Always (check to) initialize implants when setting up an avatar or becoming fatigued or when revived.
|
|
||||||
* @param slot the slot in which this implant is found
|
|
||||||
*/
|
|
||||||
def ImplantInitializationStart(slot: Int): Unit = {
|
|
||||||
val implantSlot = player.ImplantSlot(slot)
|
|
||||||
if (implantSlot.Installed.isDefined) {
|
|
||||||
if (!implantSlot.Initialized) {
|
|
||||||
val time = System.currentTimeMillis
|
|
||||||
val initializationTime = if (implantSlot.InitializeTime == 0L) {
|
|
||||||
implantSlot.InitializeTime = time
|
|
||||||
time
|
|
||||||
} else {
|
|
||||||
implantSlot.InitializeTime
|
|
||||||
}
|
|
||||||
val maxInitializationTime = implantSlot.MaxTimer * 1000
|
|
||||||
if (time - initializationTime > maxInitializationTime) {
|
|
||||||
//this implant should have already been initialized
|
|
||||||
ImplantInitializationComplete(slot)
|
|
||||||
} else {
|
|
||||||
// Start client side initialization timer
|
|
||||||
// Check this along the bottom of the character information window
|
|
||||||
//progress accumulates according to the client's knowledge of the implant initialization time
|
|
||||||
//what is normally a 60s timer that is set to 120s on the server will still visually update as if 60s
|
|
||||||
val percent = (100 * (time - initializationTime) / maxInitializationTime.toFloat).toInt
|
|
||||||
player.Zone.AvatarEvents ! AvatarServiceMessage(
|
|
||||||
player.Name,
|
|
||||||
AvatarAction.SendResponse(Service.defaultPlayerGUID, ActionProgressMessage(slot + 6, percent))
|
|
||||||
)
|
|
||||||
// Callback after initialization timer to complete initialization
|
|
||||||
implantSlotTimers(slot).cancel
|
|
||||||
implantSlotTimers(slot) = context.system.scheduler.scheduleOnce(
|
|
||||||
(maxInitializationTime - (time - initializationTime)) milliseconds,
|
|
||||||
self,
|
|
||||||
Player.ImplantInitializationComplete(slot)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ImplantInitializationComplete(slot)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The implant is ready to be made available and active on selection.
|
|
||||||
* The end result of a timed process, occasionally an implant will become "already active".
|
|
||||||
* @param slot the slot in which this implant is found
|
|
||||||
*/
|
|
||||||
def ImplantInitializationComplete(slot: Int): Unit = {
|
|
||||||
val implantSlot = player.ImplantSlot(slot)
|
|
||||||
if (implantSlot.Installed.isDefined) {
|
|
||||||
implantSlot.Initialized = true
|
|
||||||
player.Zone.AvatarEvents ! AvatarServiceMessage(
|
|
||||||
player.Name,
|
|
||||||
AvatarAction.SendResponse(
|
|
||||||
Service.defaultPlayerGUID,
|
|
||||||
AvatarImplantMessage(player.GUID, ImplantAction.Initialization, slot, 1)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
implantSlotTimers(slot).cancel
|
|
||||||
implantSlotTimers(slot) = Default.Cancellable
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether or not the implant is being used by the player who installed it.
|
|
||||||
* If the implant has no business having its activation state changed yet, it (re)starts its initialization phase.
|
|
||||||
* @param slot the slot in which this implant is found
|
|
||||||
* @param status `1`, if the implant should become active;
|
|
||||||
* `0`, if it should be deactivated
|
|
||||||
*/
|
|
||||||
def ImplantActivation(slot: Int, status: Int): Unit = {
|
|
||||||
val implantSlot = player.ImplantSlot(slot)
|
|
||||||
if (!implantSlot.Initialized && !player.Fatigued) {
|
|
||||||
log.warn(s"implant in slot $slot is trying to (de)activate when not even initialized!")
|
|
||||||
//we should not be activating or deactivataing, but initializing
|
|
||||||
implantSlotTimers(slot).cancel
|
|
||||||
implantSlotTimers(slot) = Default.Cancellable
|
|
||||||
implantSlot.Active = false
|
|
||||||
//normal deactivation
|
|
||||||
player.Zone.AvatarEvents ! AvatarServiceMessage(
|
|
||||||
player.Name,
|
|
||||||
AvatarAction.DeactivateImplantSlot(player.GUID, slot)
|
|
||||||
)
|
|
||||||
//initialization process (from scratch)
|
|
||||||
implantSlot.InitializeTime = 0
|
|
||||||
ImplantInitializationStart(slot)
|
|
||||||
} else if (status == 0 && implantSlot.Active) {
|
|
||||||
implantSlotTimers(slot).cancel
|
|
||||||
implantSlotTimers(slot) = Default.Cancellable
|
|
||||||
implantSlot.Active = false
|
|
||||||
player.Zone.AvatarEvents ! AvatarServiceMessage(
|
|
||||||
player.Zone.Id,
|
|
||||||
AvatarAction.PlanetsideAttribute(player.GUID, 28, player.Implant(slot).id * 2)
|
|
||||||
) // Deactivation sound / effect
|
|
||||||
player.Zone.AvatarEvents ! AvatarServiceMessage(
|
|
||||||
player.Name,
|
|
||||||
AvatarAction.DeactivateImplantSlot(player.GUID, slot)
|
|
||||||
)
|
|
||||||
} else if (status == 1 && implantSlot.Initialized && !player.Fatigued) {
|
|
||||||
implantSlot.Installed match {
|
|
||||||
case Some(implant)
|
|
||||||
if (implant.Type == ImplantType.PersonalShield && player.ExoSuit == ExoSuitType.Infiltration) ||
|
|
||||||
(implant.Type == ImplantType.Surge && player.ExoSuit == ExoSuitType.MAX) =>
|
|
||||||
//TODO STILL NOT ALLOWED (but make it look normal)
|
|
||||||
case Some(implant) =>
|
|
||||||
if (implantSlot.Active) {
|
|
||||||
// Some events such as zoning will reset the implant on the client side without sending a deactivation packet
|
|
||||||
// But the implant will remain in an active state server side. For now, allow reactivation of the implant.
|
|
||||||
log.warn(s"implant $slot is already active, but activating again")
|
|
||||||
implantSlotTimers(slot).cancel
|
|
||||||
implantSlotTimers(slot) = Default.Cancellable
|
|
||||||
}
|
|
||||||
val activationStaminaCost = implant.ActivationStaminaCost
|
|
||||||
if (activationStaminaCost > 0) {
|
|
||||||
player.Stamina -= activationStaminaCost // Activation stamina drain
|
|
||||||
UpdateStamina()
|
|
||||||
}
|
|
||||||
if (!player.Fatigued) {
|
|
||||||
implantSlot.Active = true
|
|
||||||
val zone = player.Zone
|
|
||||||
val drainInterval = implant.GetCostIntervalByExoSuit(player.ExoSuit)
|
|
||||||
if (drainInterval > 0) { // Ongoing stamina drain, if applicable
|
|
||||||
implantSlotTimers(slot).cancel
|
|
||||||
implantSlotTimers(slot) = context.system.scheduler.scheduleWithFixedDelay(
|
|
||||||
initialDelay = 0 seconds,
|
|
||||||
drainInterval milliseconds,
|
|
||||||
self,
|
|
||||||
Player.StaminaChanged(-implant.StaminaCost)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
zone.AvatarEvents ! AvatarServiceMessage(
|
|
||||||
zone.Id,
|
|
||||||
AvatarAction.PlanetsideAttribute(player.GUID, 28, player.Implant(slot).id * 2 + 1)
|
|
||||||
) // Activation sound / effect
|
|
||||||
zone.AvatarEvents ! AvatarServiceMessage(player.Name, AvatarAction.ActivateImplantSlot(player.GUID, slot))
|
|
||||||
}
|
|
||||||
case _ =>
|
|
||||||
//there should have been an implant here ...
|
|
||||||
implantSlot.Active = false
|
|
||||||
implantSlot.Initialized = false
|
|
||||||
implantSlot.InitializeTime = 0L
|
|
||||||
//todo: AvatarImplantMessage(tplayer.GUID, ImplantAction.Remove, slot, 0)?
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The implant in this slot is no longer active and is no longer considered ready to activate.
|
|
||||||
* @param slot the slot in which an implant could be found
|
|
||||||
*/
|
|
||||||
def UninitializeImplant(slot: Int): Unit = {
|
|
||||||
implantSlotTimers(slot).cancel
|
|
||||||
implantSlotTimers(slot) = Default.Cancellable
|
|
||||||
val zone = player.Zone
|
|
||||||
val guid = player.GUID
|
|
||||||
val playerChannel = player.Name
|
|
||||||
val zoneChannel = zone.Id
|
|
||||||
val implantSlot = player.ImplantSlot(slot)
|
|
||||||
// if(implantSlot.Active) {
|
|
||||||
// zone.AvatarEvents ! AvatarServiceMessage(zoneChannel, AvatarAction.PlanetsideAttribute(guid, 28, player.Implant(slot).id * 2)) // Deactivation sound / effect
|
|
||||||
// zone.AvatarEvents ! AvatarServiceMessage(playerChannel, AvatarAction.DeactivateImplantSlot(guid, slot))
|
|
||||||
// }
|
|
||||||
implantSlot.Active = false
|
|
||||||
implantSlot.Initialized = false
|
|
||||||
implantSlot.InitializeTime = 0L
|
|
||||||
zone.AvatarEvents ! AvatarServiceMessage(
|
|
||||||
playerChannel,
|
|
||||||
AvatarAction.SendResponse(Service.defaultPlayerGUID, ActionProgressMessage(slot + 6, 100))
|
|
||||||
)
|
|
||||||
zone.AvatarEvents ! AvatarServiceMessage(
|
|
||||||
zoneChannel,
|
|
||||||
AvatarAction.SendResponse(
|
|
||||||
Service.defaultPlayerGUID,
|
|
||||||
AvatarImplantMessage(guid, ImplantAction.Initialization, slot, 0)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
object PlayerControl {
|
|
||||||
|
|
||||||
/**
|
|
||||||
*/
|
|
||||||
private case class StaminaRegen()
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -73,12 +73,12 @@ final case class Projectile(
|
||||||
object Projectile {
|
object Projectile {
|
||||||
|
|
||||||
/** the first projectile GUID used by all clients internally */
|
/** the first projectile GUID used by all clients internally */
|
||||||
final val BaseUID: Int = 40100
|
final val baseUID: Int = 40100
|
||||||
|
|
||||||
/** all clients progress through 40100 to 40124 normally, skipping only for long-lived projectiles
|
/** all clients progress through 40100 to 40124 normally, skipping only for long-lived projectiles
|
||||||
* 40125 to 40149 are being reserved as a guard against undetected overflow
|
* 40125 to 40149 are being reserved as a guard against undetected overflow
|
||||||
*/
|
*/
|
||||||
final val RangeUID: Int = 40150
|
final val rangeUID: Int = 40150
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Overloaded constructor for an `Unresolved` projectile.
|
* Overloaded constructor for an `Unresolved` projectile.
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
// Copyright (c) 2017 PSForever
|
// Copyright (c) 2017 PSForever
|
||||||
package net.psforever.objects.definition
|
package net.psforever.objects.definition
|
||||||
|
|
||||||
|
import net.psforever.objects.avatar.Certification
|
||||||
import net.psforever.objects.ce.DeployedItem
|
import net.psforever.objects.ce.DeployedItem
|
||||||
import net.psforever.objects.definition.converter.ACEConverter
|
import net.psforever.objects.definition.converter.ACEConverter
|
||||||
import net.psforever.objects.equipment.CItem
|
import net.psforever.objects.equipment.CItem
|
||||||
import net.psforever.types.CertificationType
|
|
||||||
|
|
||||||
import scala.collection.mutable.ListBuffer
|
import scala.collection.mutable.ListBuffer
|
||||||
|
|
||||||
|
|
@ -27,21 +27,20 @@ object ConstructionItemDefinition {
|
||||||
}
|
}
|
||||||
|
|
||||||
class ConstructionFireMode {
|
class ConstructionFireMode {
|
||||||
private val deployables: ListBuffer[DeployedItem.Value] = ListBuffer.empty
|
private val deployables: ListBuffer[DeployedItem.Value] = ListBuffer.empty
|
||||||
private val permissions: ListBuffer[Set[CertificationType.Value]] = ListBuffer.empty
|
private val permissions: ListBuffer[Set[Certification]] = ListBuffer.empty
|
||||||
|
|
||||||
def Permissions: ListBuffer[Set[CertificationType.Value]] = permissions
|
def Permissions: ListBuffer[Set[Certification]] = permissions
|
||||||
|
|
||||||
def Deployables: ListBuffer[DeployedItem.Value] = deployables
|
def Deployables: ListBuffer[DeployedItem.Value] = deployables
|
||||||
|
|
||||||
def Item(deployable: DeployedItem.Value): ListBuffer[DeployedItem.Value] = {
|
def Item(deployable: DeployedItem.Value): ListBuffer[DeployedItem.Value] = {
|
||||||
deployables += deployable
|
deployables += deployable
|
||||||
permissions += Set.empty[CertificationType.Value]
|
permissions += Set.empty[Certification]
|
||||||
deployables
|
deployables
|
||||||
}
|
}
|
||||||
|
|
||||||
def Item(deployPair: (DeployedItem.Value, Set[CertificationType.Value])): ListBuffer[DeployedItem.Value] = {
|
def Item(deployable: DeployedItem.Value, permission: Set[Certification]): ListBuffer[DeployedItem.Value] = {
|
||||||
val (deployable, permission) = deployPair
|
|
||||||
deployables += deployable
|
deployables += deployable
|
||||||
permissions += permission
|
permissions += permission
|
||||||
deployables
|
deployables
|
||||||
|
|
|
||||||
|
|
@ -2,12 +2,13 @@
|
||||||
package net.psforever.objects.definition
|
package net.psforever.objects.definition
|
||||||
|
|
||||||
import net.psforever.objects.GlobalDefinitions
|
import net.psforever.objects.GlobalDefinitions
|
||||||
|
import net.psforever.objects.avatar.Certification
|
||||||
import net.psforever.objects.equipment.EquipmentSize
|
import net.psforever.objects.equipment.EquipmentSize
|
||||||
import net.psforever.objects.inventory.InventoryTile
|
import net.psforever.objects.inventory.InventoryTile
|
||||||
import net.psforever.objects.vital._
|
import net.psforever.objects.vital._
|
||||||
import net.psforever.objects.vital.damage.DamageCalculations
|
import net.psforever.objects.vital.damage.DamageCalculations
|
||||||
import net.psforever.objects.vital.resistance.ResistanceProfileMutators
|
import net.psforever.objects.vital.resistance.ResistanceProfileMutators
|
||||||
import net.psforever.types.{CertificationType, ExoSuitType, PlanetSideEmpire}
|
import net.psforever.types.{ExoSuitType, PlanetSideEmpire}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A definition for producing the personal armor the player wears.
|
* A definition for producing the personal armor the player wears.
|
||||||
|
|
@ -18,15 +19,15 @@ class ExoSuitDefinition(private val suitType: ExoSuitType.Value)
|
||||||
extends BasicDefinition
|
extends BasicDefinition
|
||||||
with ResistanceProfileMutators
|
with ResistanceProfileMutators
|
||||||
with DamageResistanceModel {
|
with DamageResistanceModel {
|
||||||
protected var permissions: List[CertificationType.Value] = List.empty
|
protected var permissions: List[Certification] = List.empty
|
||||||
protected var maxArmor: Int = 0
|
protected var maxArmor: Int = 0
|
||||||
protected val holsters: Array[EquipmentSize.Value] = Array.fill[EquipmentSize.Value](5)(EquipmentSize.Blocked)
|
protected val holsters: Array[EquipmentSize.Value] = Array.fill[EquipmentSize.Value](5)(EquipmentSize.Blocked)
|
||||||
protected var inventoryScale: InventoryTile = InventoryTile.Tile11 //override with custom InventoryTile
|
protected var inventoryScale: InventoryTile = InventoryTile.Tile11 //override with custom InventoryTile
|
||||||
protected var inventoryOffset: Int = 0
|
protected var inventoryOffset: Int = 0
|
||||||
protected var maxCapacitor: Int = 0
|
protected var maxCapacitor: Int = 0
|
||||||
protected var capacitorRechargeDelayMillis: Int = 0
|
protected var capacitorRechargeDelayMillis: Int = 0
|
||||||
protected var capacitorRechargePerSecond: Int = 0
|
protected var capacitorRechargePerSecond: Int = 0
|
||||||
protected var capacitorDrainPerSecond: Int = 0
|
protected var capacitorDrainPerSecond: Int = 0
|
||||||
Name = "exo-suit"
|
Name = "exo-suit"
|
||||||
DamageUsing = DamageCalculations.AgainstExoSuit
|
DamageUsing = DamageCalculations.AgainstExoSuit
|
||||||
ResistUsing = StandardInfantryResistance
|
ResistUsing = StandardInfantryResistance
|
||||||
|
|
@ -102,25 +103,15 @@ class ExoSuitDefinition(private val suitType: ExoSuitType.Value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def Permissions: List[CertificationType.Value] = permissions
|
def Permissions: List[Certification] = permissions
|
||||||
|
|
||||||
def Permissions_=(certs: List[CertificationType.Value]): List[CertificationType.Value] = {
|
def Permissions_=(certs: List[Certification]): List[Certification] = {
|
||||||
permissions = certs
|
permissions = certs
|
||||||
Permissions
|
Permissions
|
||||||
}
|
}
|
||||||
|
|
||||||
def Use: ExoSuitDefinition = this
|
def Use: ExoSuitDefinition = this
|
||||||
|
|
||||||
def canEqual(other: Any): Boolean = other.isInstanceOf[ExoSuitDefinition]
|
|
||||||
|
|
||||||
override def equals(other: Any): Boolean =
|
|
||||||
other match {
|
|
||||||
case that: ExoSuitDefinition =>
|
|
||||||
(that canEqual this) &&
|
|
||||||
suitType == that.suitType
|
|
||||||
case _ => false
|
|
||||||
}
|
|
||||||
|
|
||||||
override def hashCode(): Int = {
|
override def hashCode(): Int = {
|
||||||
val state = Seq(suitType)
|
val state = Seq(suitType)
|
||||||
state.map(_.hashCode()).foldLeft(0)((a, b) => 31 * a + b)
|
state.map(_.hashCode()).foldLeft(0)((a, b) => 31 * a + b)
|
||||||
|
|
|
||||||
|
|
@ -14,11 +14,11 @@ import scala.collection.mutable
|
||||||
* Passive implants are always active and thus have no cost.
|
* Passive implants are always active and thus have no cost.
|
||||||
* After being activated, a non-passive implant consumes a specific amount of stamina at regular intervals
|
* After being activated, a non-passive implant consumes a specific amount of stamina at regular intervals
|
||||||
* Some implants will specify a different interval for consuming stamina based on the exo-suit the player is wearing
|
* Some implants will specify a different interval for consuming stamina based on the exo-suit the player is wearing
|
||||||
|
*
|
||||||
* @param implantType the type of implant that is defined
|
* @param implantType the type of implant that is defined
|
||||||
* @see `ImplantType`
|
* @see `ImplantType`
|
||||||
*/
|
*/
|
||||||
class ImplantDefinition(private val implantType: Int) extends BasicDefinition {
|
class ImplantDefinition(val implantType: ImplantType) extends BasicDefinition {
|
||||||
ImplantType(implantType)
|
|
||||||
|
|
||||||
/** how long it takes the implant to become ready for activation; is milliseconds */
|
/** how long it takes the implant to become ready for activation; is milliseconds */
|
||||||
private var initializationDuration: Long = 0L
|
private var initializationDuration: Long = 0L
|
||||||
|
|
@ -84,16 +84,4 @@ class ImplantDefinition(private val implantType: Int) extends BasicDefinition {
|
||||||
def GetCostIntervalByExoSuit(exosuit: ExoSuitType.Value): Int =
|
def GetCostIntervalByExoSuit(exosuit: ExoSuitType.Value): Int =
|
||||||
costIntervalByExoSuit.getOrElse(exosuit, CostIntervalDefault)
|
costIntervalByExoSuit.getOrElse(exosuit, CostIntervalDefault)
|
||||||
def CostIntervalByExoSuitHashMap: mutable.Map[ExoSuitType.Value, Int] = costIntervalByExoSuit
|
def CostIntervalByExoSuitHashMap: mutable.Map[ExoSuitType.Value, Int] = costIntervalByExoSuit
|
||||||
|
|
||||||
def Type: ImplantType.Value = ImplantType(implantType)
|
|
||||||
}
|
|
||||||
|
|
||||||
object ImplantDefinition {
|
|
||||||
def apply(implantType: Int): ImplantDefinition = {
|
|
||||||
new ImplantDefinition(implantType)
|
|
||||||
}
|
|
||||||
|
|
||||||
def apply(implantType: ImplantType.Value): ImplantDefinition = {
|
|
||||||
new ImplantDefinition(implantType.id)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ package net.psforever.objects.definition.converter
|
||||||
import net.psforever.objects.Player
|
import net.psforever.objects.Player
|
||||||
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, ImplantType, PlanetSideGUID}
|
import net.psforever.types.{ExoSuitType, GrenadeState, PlanetSideGUID}
|
||||||
|
|
||||||
import scala.annotation.tailrec
|
import scala.annotation.tailrec
|
||||||
import scala.util.{Success, Try}
|
import scala.util.{Success, Try}
|
||||||
|
|
@ -96,13 +96,13 @@ object AvatarConverter {
|
||||||
false,
|
false,
|
||||||
facingPitch = obj.Orientation.y,
|
facingPitch = obj.Orientation.y,
|
||||||
facingYawUpper = obj.FacingYawUpper,
|
facingYawUpper = obj.FacingYawUpper,
|
||||||
obj.LFS,
|
obj.avatar.lookingForSquad,
|
||||||
GrenadeState.None,
|
GrenadeState.None,
|
||||||
obj.Cloaked,
|
obj.Cloaked,
|
||||||
false,
|
unk5 = false,
|
||||||
false,
|
unk6 = false,
|
||||||
charging_pose = false,
|
charging_pose = false,
|
||||||
false,
|
unk7 = false,
|
||||||
on_zipline = None
|
on_zipline = None
|
||||||
)
|
)
|
||||||
CharacterAppearanceData(aa, ab, RibbonBars())
|
CharacterAppearanceData(aa, ab, RibbonBars())
|
||||||
|
|
@ -117,44 +117,43 @@ object AvatarConverter {
|
||||||
} else {
|
} else {
|
||||||
StatConverter.Health(obj.Armor, MaxArmor)
|
StatConverter.Health(obj.Armor, MaxArmor)
|
||||||
},
|
},
|
||||||
DressBattleRank(obj),
|
obj.avatar.br.uniformStyle,
|
||||||
0,
|
0,
|
||||||
DressCommandRank(obj),
|
obj.avatar.cr.value,
|
||||||
MakeImplantEffectList(obj.Implants.toIndexedSeq),
|
obj.avatar.implants.flatten.filter(_.active).flatMap(_.definition.implantType.effect).toList,
|
||||||
MakeCosmetics(obj)
|
obj.avatar.cosmetics
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
def MakeDetailedCharacterData(obj: Player): Option[Int] => DetailedCharacterData = {
|
def MakeDetailedCharacterData(obj: Player): Option[Int] => DetailedCharacterData = {
|
||||||
val bep: Long = obj.BEP
|
|
||||||
val maxOpt: Option[Long] = if (obj.ExoSuit == ExoSuitType.MAX) { Some(0L) }
|
val maxOpt: Option[Long] = if (obj.ExoSuit == ExoSuitType.MAX) { Some(0L) }
|
||||||
else { None }
|
else { None }
|
||||||
val ba: DetailedCharacterA = DetailedCharacterA(
|
val ba: DetailedCharacterA = DetailedCharacterA(
|
||||||
bep,
|
obj.avatar.bep,
|
||||||
obj.CEP,
|
obj.avatar.cep,
|
||||||
0L,
|
0L,
|
||||||
0L,
|
0L,
|
||||||
0L,
|
0L,
|
||||||
obj.MaxHealth,
|
obj.MaxHealth,
|
||||||
obj.Health,
|
obj.Health,
|
||||||
false,
|
unk4 = false,
|
||||||
obj.Armor,
|
obj.Armor,
|
||||||
0L,
|
0L,
|
||||||
obj.MaxStamina,
|
obj.avatar.maxStamina,
|
||||||
obj.Stamina,
|
obj.avatar.stamina,
|
||||||
maxOpt,
|
maxOpt,
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
0L,
|
0L,
|
||||||
List(0, 0, 0, 0, 0, 0),
|
List(0, 0, 0, 0, 0, 0),
|
||||||
obj.Certifications.toList.sortBy(_.id) //TODO is sorting necessary?
|
obj.avatar.certifications.toList.sortBy(_.value) //TODO is sorting necessary?
|
||||||
)
|
)
|
||||||
val bb: (Long, Option[Int]) => DetailedCharacterB = DetailedCharacterB(
|
val bb: (Long, Option[Int]) => DetailedCharacterB = DetailedCharacterB(
|
||||||
None,
|
None,
|
||||||
MakeImplantEntries(obj),
|
obj.avatar.implants.flatten.map(_.toEntry).toList,
|
||||||
Nil,
|
Nil,
|
||||||
Nil,
|
Nil,
|
||||||
obj.FirstTimeEvents,
|
obj.avatar.firstTimeEvents.toList,
|
||||||
tutorials = List.empty[String], //TODO tutorial list
|
tutorials = List.empty[String], //TODO tutorial list
|
||||||
0L,
|
0L,
|
||||||
0L,
|
0L,
|
||||||
|
|
@ -165,9 +164,9 @@ object AvatarConverter {
|
||||||
Nil,
|
Nil,
|
||||||
Nil,
|
Nil,
|
||||||
false,
|
false,
|
||||||
MakeCosmetics(obj)
|
obj.avatar.cosmetics
|
||||||
)
|
)
|
||||||
pad_length: Option[Int] => DetailedCharacterData(ba, bb(bep, pad_length))(pad_length)
|
pad_length: Option[Int] => DetailedCharacterData(ba, bb(obj.avatar.bep, pad_length))(pad_length)
|
||||||
}
|
}
|
||||||
|
|
||||||
def MakeInventoryData(obj: Player): InventoryData = {
|
def MakeInventoryData(obj: Player): InventoryData = {
|
||||||
|
|
@ -180,98 +179,6 @@ object AvatarConverter {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Select the appropriate `UniformStyle` design for a player's accumulated battle experience points.
|
|
||||||
* At certain battle ranks, all exo-suits undergo some form of coloration change.
|
|
||||||
* @param obj the `Player` game object
|
|
||||||
* @return the resulting uniform upgrade level
|
|
||||||
*/
|
|
||||||
private def DressBattleRank(obj: Player): UniformStyle.Value = {
|
|
||||||
val bep: Long = obj.BEP
|
|
||||||
if (bep > 2583440) { //BR25+
|
|
||||||
UniformStyle.ThirdUpgrade
|
|
||||||
} else if (bep > 308989) { //BR14+
|
|
||||||
UniformStyle.SecondUpgrade
|
|
||||||
} else if (bep > 44999) { //BR7+
|
|
||||||
UniformStyle.FirstUpgrade
|
|
||||||
} else { //BR1+
|
|
||||||
UniformStyle.Normal
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Select the appropriate design for a player's accumulated command experience points.
|
|
||||||
* Visual cues for command rank include armlets, anklets, and, finally, a backpack, awarded at different ranks.
|
|
||||||
* @param obj the `Player` game object
|
|
||||||
* @return the resulting uniform upgrade level
|
|
||||||
*/
|
|
||||||
private def DressCommandRank(obj: Player): Int = {
|
|
||||||
val cep = obj.CEP
|
|
||||||
if (cep > 599999) {
|
|
||||||
5
|
|
||||||
} else if (cep > 299999) {
|
|
||||||
4
|
|
||||||
} else if (cep > 149999) {
|
|
||||||
3
|
|
||||||
} else if (cep > 49999) {
|
|
||||||
2
|
|
||||||
} else if (cep > 9999) {
|
|
||||||
1
|
|
||||||
} else {
|
|
||||||
0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Transform an `Array` of `Implant` objects into a `List` of `ImplantEntry` objects suitable as packet data.
|
|
||||||
* @param obj the `Player` game object
|
|
||||||
* @return the resulting implant `List`
|
|
||||||
* @see `ImplantEntry` in `DetailedCharacterData`
|
|
||||||
*/
|
|
||||||
private def MakeImplantEntries(obj: Player): List[ImplantEntry] = {
|
|
||||||
//val numImplants : Int = DetailedCharacterData.numberOfImplantSlots(obj.BEP)
|
|
||||||
//val implants = obj.Implants
|
|
||||||
obj.Implants
|
|
||||||
.map({
|
|
||||||
case (implant, initialization, _) =>
|
|
||||||
if (initialization == 0) {
|
|
||||||
ImplantEntry(implant, None)
|
|
||||||
} else {
|
|
||||||
ImplantEntry(implant, Some(math.max(0, initialization).toInt))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.toList
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Find and encode implants whose effect will be displayed on this player.
|
|
||||||
* @param implants a `Sequence` of `ImplantSlot` objects
|
|
||||||
* @return the effect of an active implant
|
|
||||||
*/
|
|
||||||
private def MakeImplantEffectList(implants: Seq[(ImplantType.Value, Long, Boolean)]): List[ImplantEffects.Value] = {
|
|
||||||
implants.collect {
|
|
||||||
case (ImplantType.AdvancedRegen, _, true) => ImplantEffects.RegenEffects
|
|
||||||
case (ImplantType.DarklightVision, _, true) => ImplantEffects.DarklightEffects
|
|
||||||
case (ImplantType.PersonalShield, _, true) => ImplantEffects.PersonalShieldEffects
|
|
||||||
case (ImplantType.Surge, _, true) => ImplantEffects.SurgeEffects
|
|
||||||
}.toList
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Should this player be of battle rank 24 or higher, they will have a mandatory cosmetics object in their bitstream.
|
|
||||||
* Players that have not yet set any cosmetic personal effects will still have this field recorded as `None`
|
|
||||||
* but it must be represented nonetheless.
|
|
||||||
* @param obj the `Player` game object
|
|
||||||
* @see `Cosmetics`
|
|
||||||
* @return the `Cosmetics` options
|
|
||||||
*/
|
|
||||||
def MakeCosmetics(obj: Player): Option[Cosmetics] =
|
|
||||||
if (DetailedCharacterData.isBR24(obj.BEP)) {
|
|
||||||
obj.PersonalStyleFeatures.orElse(Some(Cosmetics()))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Given a player with an inventory, convert the contents of that inventory into converted-decoded packet data.
|
* Given a player with an inventory, convert the contents of that inventory into converted-decoded packet data.
|
||||||
* The inventory is not represented in a `0x17` `Player`, so the conversion is only valid for `0x18` avatars.
|
* The inventory is not represented in a `0x17` `Player`, so the conversion is only valid for `0x18` avatars.
|
||||||
|
|
@ -363,7 +270,7 @@ object AvatarConverter {
|
||||||
if (!iter.hasNext) {
|
if (!iter.hasNext) {
|
||||||
list
|
list
|
||||||
} else {
|
} else {
|
||||||
val slot: EquipmentSlot = iter.next
|
val slot: EquipmentSlot = iter.next()
|
||||||
if (slot.Equipment.isDefined) {
|
if (slot.Equipment.isDefined) {
|
||||||
val equip: Equipment = slot.Equipment.get
|
val equip: Equipment = slot.Equipment.get
|
||||||
recursiveMakeHolsters(
|
recursiveMakeHolsters(
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
// Copyright (c) 2017 PSForever
|
// Copyright (c) 2017 PSForever
|
||||||
package net.psforever.objects.definition.converter
|
package net.psforever.objects.definition.converter
|
||||||
|
|
||||||
|
import net.psforever.objects.avatar.Certification
|
||||||
import net.psforever.objects.{Player, Tool}
|
import net.psforever.objects.{Player, Tool}
|
||||||
import net.psforever.objects.equipment.EquipmentSlot
|
import net.psforever.objects.equipment.EquipmentSlot
|
||||||
import net.psforever.packet.game.objectcreate._
|
import net.psforever.packet.game.objectcreate._
|
||||||
|
|
@ -42,10 +43,10 @@ class CharacterSelectConverter extends AvatarConverter {
|
||||||
CommonFieldData(
|
CommonFieldData(
|
||||||
obj.Faction,
|
obj.Faction,
|
||||||
bops = false,
|
bops = false,
|
||||||
false,
|
alternate = false,
|
||||||
false,
|
v1 = false,
|
||||||
None,
|
None,
|
||||||
false,
|
jammered = false,
|
||||||
None,
|
None,
|
||||||
v5 = None,
|
v5 = None,
|
||||||
PlanetSideGUID(0)
|
PlanetSideGUID(0)
|
||||||
|
|
@ -62,38 +63,37 @@ class CharacterSelectConverter extends AvatarConverter {
|
||||||
0L,
|
0L,
|
||||||
outfit_name = "",
|
outfit_name = "",
|
||||||
outfit_logo = 0,
|
outfit_logo = 0,
|
||||||
false,
|
unk1 = false,
|
||||||
backpack = false,
|
backpack = false,
|
||||||
false,
|
unk2 = false,
|
||||||
false,
|
unk3 = false,
|
||||||
false,
|
unk4 = false,
|
||||||
facingPitch = 0,
|
facingPitch = 0,
|
||||||
facingYawUpper = 0,
|
facingYawUpper = 0,
|
||||||
lfs = false,
|
lfs = false,
|
||||||
GrenadeState.None,
|
GrenadeState.None,
|
||||||
obj.Cloaked,
|
is_cloaking = obj.Cloaked,
|
||||||
false,
|
unk5 = false,
|
||||||
false,
|
unk6 = false,
|
||||||
charging_pose = false,
|
charging_pose = false,
|
||||||
false,
|
unk7 = false,
|
||||||
on_zipline = None
|
on_zipline = None
|
||||||
)
|
)
|
||||||
CharacterAppearanceData(aa, ab, RibbonBars())
|
CharacterAppearanceData(aa, ab, RibbonBars())
|
||||||
}
|
}
|
||||||
|
|
||||||
private def MakeDetailedCharacterData(obj: Player): Option[Int] => DetailedCharacterData = {
|
private def MakeDetailedCharacterData(obj: Player): Option[Int] => DetailedCharacterData = {
|
||||||
val bep: Long = obj.BEP
|
|
||||||
val maxOpt: Option[Long] = if (obj.ExoSuit == ExoSuitType.MAX) { Some(0L) }
|
val maxOpt: Option[Long] = if (obj.ExoSuit == ExoSuitType.MAX) { Some(0L) }
|
||||||
else { None }
|
else { None }
|
||||||
val ba: DetailedCharacterA = DetailedCharacterA(
|
val ba: DetailedCharacterA = DetailedCharacterA(
|
||||||
bep,
|
obj.avatar.bep,
|
||||||
obj.CEP,
|
obj.avatar.cep,
|
||||||
0L,
|
0L,
|
||||||
0L,
|
0L,
|
||||||
0L,
|
0L,
|
||||||
1,
|
1,
|
||||||
1,
|
1,
|
||||||
false,
|
unk4 = false,
|
||||||
0,
|
0,
|
||||||
0L,
|
0L,
|
||||||
1,
|
1,
|
||||||
|
|
@ -103,11 +103,13 @@ class CharacterSelectConverter extends AvatarConverter {
|
||||||
0,
|
0,
|
||||||
0L,
|
0L,
|
||||||
List(0, 0, 0, 0, 0, 0),
|
List(0, 0, 0, 0, 0, 0),
|
||||||
certs = List.empty[CertificationType.Value]
|
certs = List.empty[Certification]
|
||||||
)
|
)
|
||||||
|
|
||||||
val bb: (Long, Option[Int]) => DetailedCharacterB = DetailedCharacterB(
|
val bb: (Long, Option[Int]) => DetailedCharacterB = DetailedCharacterB(
|
||||||
None,
|
None,
|
||||||
MakeImplantEntries(obj), //necessary for correct stream length
|
// necessary for correct stream length
|
||||||
|
List.fill[ImplantEntry](obj.avatar.br.implantSlots)(ImplantEntry(ImplantType.None, None)),
|
||||||
Nil,
|
Nil,
|
||||||
Nil,
|
Nil,
|
||||||
firstTimeEvents = List.empty[String],
|
firstTimeEvents = List.empty[String],
|
||||||
|
|
@ -120,20 +122,10 @@ class CharacterSelectConverter extends AvatarConverter {
|
||||||
Some(DCDExtra2(0, 0)),
|
Some(DCDExtra2(0, 0)),
|
||||||
Nil,
|
Nil,
|
||||||
Nil,
|
Nil,
|
||||||
false,
|
unkC = false,
|
||||||
AvatarConverter.MakeCosmetics(obj)
|
obj.avatar.cosmetics
|
||||||
)
|
)
|
||||||
pad_length: Option[Int] => DetailedCharacterData(ba, bb(bep, pad_length))(pad_length)
|
pad_length: Option[Int] => DetailedCharacterData(ba, bb(obj.avatar.bep, pad_length))(pad_length)
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Transform an `Array` of `Implant` objects into a `List` of `ImplantEntry` objects suitable as packet data.
|
|
||||||
* @param obj the `Player` game object
|
|
||||||
* @return the resulting implant `List`
|
|
||||||
* @see `ImplantEntry` in `DetailedCharacterData`
|
|
||||||
*/
|
|
||||||
private def MakeImplantEntries(obj: Player): List[ImplantEntry] = {
|
|
||||||
List.fill[ImplantEntry](DetailedCharacterData.numberOfImplantSlots(obj.BEP))(ImplantEntry(ImplantType.None, None))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -152,7 +144,7 @@ class CharacterSelectConverter extends AvatarConverter {
|
||||||
if (!iter.hasNext) {
|
if (!iter.hasNext) {
|
||||||
list
|
list
|
||||||
} else {
|
} else {
|
||||||
val slot: EquipmentSlot = iter.next
|
val slot: EquipmentSlot = iter.next()
|
||||||
slot.Equipment match {
|
slot.Equipment match {
|
||||||
case Some(equip: Tool) =>
|
case Some(equip: Tool) =>
|
||||||
val jammed = equip.Jammed
|
val jammed = equip.Jammed
|
||||||
|
|
|
||||||
|
|
@ -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.Certification
|
||||||
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.{PlanetSideGUID, _}
|
import net.psforever.types.{PlanetSideGUID, _}
|
||||||
|
|
@ -37,9 +38,9 @@ class CorpseConverter extends AvatarConverter {
|
||||||
obj.Faction,
|
obj.Faction,
|
||||||
bops = false,
|
bops = false,
|
||||||
alternate = true,
|
alternate = true,
|
||||||
false,
|
v1 = false,
|
||||||
None,
|
None,
|
||||||
false,
|
jammered = false,
|
||||||
None,
|
None,
|
||||||
v5 = None,
|
v5 = None,
|
||||||
PlanetSideGUID(0)
|
PlanetSideGUID(0)
|
||||||
|
|
@ -56,20 +57,20 @@ class CorpseConverter extends AvatarConverter {
|
||||||
0L,
|
0L,
|
||||||
outfit_name = "",
|
outfit_name = "",
|
||||||
outfit_logo = 0,
|
outfit_logo = 0,
|
||||||
false,
|
unk1 = false,
|
||||||
backpack = true,
|
backpack = true,
|
||||||
false,
|
unk2 = false,
|
||||||
false,
|
unk3 = false,
|
||||||
false,
|
unk4 = false,
|
||||||
facingPitch = 0,
|
facingPitch = 0,
|
||||||
facingYawUpper = 0,
|
facingYawUpper = 0,
|
||||||
lfs = false,
|
lfs = false,
|
||||||
GrenadeState.None,
|
GrenadeState.None,
|
||||||
is_cloaking = false,
|
is_cloaking = false,
|
||||||
false,
|
unk5 = false,
|
||||||
false,
|
unk6 = false,
|
||||||
charging_pose = false,
|
charging_pose = false,
|
||||||
false,
|
unk7 = false,
|
||||||
on_zipline = None
|
on_zipline = None
|
||||||
)
|
)
|
||||||
CharacterAppearanceData(aa, ab, RibbonBars())
|
CharacterAppearanceData(aa, ab, RibbonBars())
|
||||||
|
|
@ -86,7 +87,7 @@ class CorpseConverter extends AvatarConverter {
|
||||||
0L,
|
0L,
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
false,
|
unk4 = false,
|
||||||
0,
|
0,
|
||||||
0L,
|
0L,
|
||||||
0,
|
0,
|
||||||
|
|
@ -96,7 +97,7 @@ class CorpseConverter extends AvatarConverter {
|
||||||
0,
|
0,
|
||||||
0L,
|
0L,
|
||||||
List(0, 0, 0, 0, 0, 0),
|
List(0, 0, 0, 0, 0, 0),
|
||||||
certs = List.empty[CertificationType.Value]
|
certs = List.empty[Certification]
|
||||||
)
|
)
|
||||||
val bb: (Long, Option[Int]) => DetailedCharacterB = DetailedCharacterB(
|
val bb: (Long, Option[Int]) => DetailedCharacterB = DetailedCharacterB(
|
||||||
None,
|
None,
|
||||||
|
|
@ -113,7 +114,7 @@ class CorpseConverter extends AvatarConverter {
|
||||||
Some(DCDExtra2(0, 0)),
|
Some(DCDExtra2(0, 0)),
|
||||||
Nil,
|
Nil,
|
||||||
Nil,
|
Nil,
|
||||||
false,
|
unkC = false,
|
||||||
cosmetics = None
|
cosmetics = None
|
||||||
)
|
)
|
||||||
(pad_length: Option[Int]) => DetailedCharacterData(ba, bb(0, pad_length))(pad_length)
|
(pad_length: Option[Int]) => DetailedCharacterData(ba, bb(0, pad_length))(pad_length)
|
||||||
|
|
@ -161,7 +162,7 @@ class CorpseConverter extends AvatarConverter {
|
||||||
if (!iter.hasNext) {
|
if (!iter.hasNext) {
|
||||||
list
|
list
|
||||||
} else {
|
} else {
|
||||||
val slot: EquipmentSlot = iter.next
|
val slot: EquipmentSlot = iter.next()
|
||||||
if (slot.Equipment.isDefined) {
|
if (slot.Equipment.isDefined) {
|
||||||
val equip: Equipment = slot.Equipment.get
|
val equip: Equipment = slot.Equipment.get
|
||||||
recursiveMakeHolsters(
|
recursiveMakeHolsters(
|
||||||
|
|
|
||||||
|
|
@ -159,7 +159,7 @@ trait JammableBehavior {
|
||||||
if (!jammedSound) {
|
if (!jammedSound) {
|
||||||
jammedSound = true
|
jammedSound = true
|
||||||
import scala.concurrent.ExecutionContext.Implicits.global
|
import scala.concurrent.ExecutionContext.Implicits.global
|
||||||
jammeredSoundTimer.cancel
|
jammeredSoundTimer.cancel()
|
||||||
jammeredSoundTimer =
|
jammeredSoundTimer =
|
||||||
context.system.scheduler.scheduleOnce(dur milliseconds, self, JammableUnit.ClearJammeredSound())
|
context.system.scheduler.scheduleOnce(dur milliseconds, self, JammableUnit.ClearJammeredSound())
|
||||||
}
|
}
|
||||||
|
|
@ -174,7 +174,7 @@ trait JammableBehavior {
|
||||||
*/
|
*/
|
||||||
def StartJammeredStatus(target: Any, dur: Int): Unit = {
|
def StartJammeredStatus(target: Any, dur: Int): Unit = {
|
||||||
JammableObject.Jammed = true
|
JammableObject.Jammed = true
|
||||||
jammeredStatusTimer.cancel
|
jammeredStatusTimer.cancel()
|
||||||
import scala.concurrent.ExecutionContext.Implicits.global
|
import scala.concurrent.ExecutionContext.Implicits.global
|
||||||
jammeredStatusTimer =
|
jammeredStatusTimer =
|
||||||
context.system.scheduler.scheduleOnce(dur milliseconds, self, JammableUnit.ClearJammeredStatus())
|
context.system.scheduler.scheduleOnce(dur milliseconds, self, JammableUnit.ClearJammeredStatus())
|
||||||
|
|
@ -188,7 +188,7 @@ trait JammableBehavior {
|
||||||
*/
|
*/
|
||||||
def CancelJammeredSound(target: Any): Unit = {
|
def CancelJammeredSound(target: Any): Unit = {
|
||||||
jammedSound = false
|
jammedSound = false
|
||||||
jammeredSoundTimer.cancel
|
jammeredSoundTimer.cancel()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -199,7 +199,7 @@ trait JammableBehavior {
|
||||||
*/
|
*/
|
||||||
def CancelJammeredStatus(target: Any): Unit = {
|
def CancelJammeredStatus(target: Any): Unit = {
|
||||||
JammableObject.Jammed = false
|
JammableObject.Jammed = false
|
||||||
jammeredStatusTimer.cancel
|
jammeredStatusTimer.cancel()
|
||||||
}
|
}
|
||||||
|
|
||||||
val jammableBehavior: Receive = {
|
val jammableBehavior: Receive = {
|
||||||
|
|
@ -230,7 +230,7 @@ trait JammableMountedWeapons extends JammableBehavior {
|
||||||
target match {
|
target match {
|
||||||
case obj: PlanetSideServerObject with MountedWeapons with JammableUnit if !jammedSound =>
|
case obj: PlanetSideServerObject with MountedWeapons with JammableUnit if !jammedSound =>
|
||||||
obj.Zone.VehicleEvents ! VehicleServiceMessage(
|
obj.Zone.VehicleEvents ! VehicleServiceMessage(
|
||||||
obj.Zone.Id,
|
obj.Zone.id,
|
||||||
VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, obj.GUID, 27, 1)
|
VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, obj.GUID, 27, 1)
|
||||||
)
|
)
|
||||||
super.StartJammeredSound(target, dur)
|
super.StartJammeredSound(target, dur)
|
||||||
|
|
@ -251,7 +251,7 @@ trait JammableMountedWeapons extends JammableBehavior {
|
||||||
target match {
|
target match {
|
||||||
case obj: PlanetSideServerObject if jammedSound =>
|
case obj: PlanetSideServerObject if jammedSound =>
|
||||||
obj.Zone.VehicleEvents ! VehicleServiceMessage(
|
obj.Zone.VehicleEvents ! VehicleServiceMessage(
|
||||||
obj.Zone.Id,
|
obj.Zone.id,
|
||||||
VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, obj.GUID, 27, 0)
|
VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, obj.GUID, 27, 0)
|
||||||
)
|
)
|
||||||
case _ => ;
|
case _ => ;
|
||||||
|
|
@ -280,7 +280,7 @@ object JammableMountedWeapons {
|
||||||
*/
|
*/
|
||||||
def JammeredStatus(target: PlanetSideServerObject with MountedWeapons, statusCode: Int): Unit = {
|
def JammeredStatus(target: PlanetSideServerObject with MountedWeapons, statusCode: Int): Unit = {
|
||||||
val zone = target.Zone
|
val zone = target.Zone
|
||||||
val zoneId = zone.Id
|
val zoneId = zone.id
|
||||||
target.Weapons.values
|
target.Weapons.values
|
||||||
.map { _.Equipment }
|
.map { _.Equipment }
|
||||||
.collect {
|
.collect {
|
||||||
|
|
|
||||||
|
|
@ -153,7 +153,7 @@ object GUIDTask {
|
||||||
*/
|
*/
|
||||||
def RegisterAvatar(tplayer: Player)(implicit guid: ActorRef): TaskResolver.GiveTask = {
|
def RegisterAvatar(tplayer: Player)(implicit guid: ActorRef): TaskResolver.GiveTask = {
|
||||||
val holsterTasks = VisibleSlotTaskBuilding(tplayer.Holsters(), RegisterEquipment)
|
val holsterTasks = VisibleSlotTaskBuilding(tplayer.Holsters(), RegisterEquipment)
|
||||||
val lockerTask = List(RegisterLocker(tplayer.Locker))
|
val lockerTask = List(RegisterLocker(tplayer.avatar.locker))
|
||||||
val inventoryTasks = RegisterInventory(tplayer)
|
val inventoryTasks = RegisterInventory(tplayer)
|
||||||
TaskResolver.GiveTask(RegisterObjectTask(tplayer).task, holsterTasks ++ lockerTask ++ inventoryTasks)
|
TaskResolver.GiveTask(RegisterObjectTask(tplayer).task, holsterTasks ++ lockerTask ++ inventoryTasks)
|
||||||
}
|
}
|
||||||
|
|
@ -311,7 +311,7 @@ object GUIDTask {
|
||||||
*/
|
*/
|
||||||
def UnregisterAvatar(tplayer: Player)(implicit guid: ActorRef): TaskResolver.GiveTask = {
|
def UnregisterAvatar(tplayer: Player)(implicit guid: ActorRef): TaskResolver.GiveTask = {
|
||||||
val holsterTasks = VisibleSlotTaskBuilding(tplayer.Holsters(), UnregisterEquipment)
|
val holsterTasks = VisibleSlotTaskBuilding(tplayer.Holsters(), UnregisterEquipment)
|
||||||
val lockerTask = List(UnregisterLocker(tplayer.Locker))
|
val lockerTask = List(UnregisterLocker(tplayer.avatar.locker))
|
||||||
val inventoryTasks = UnregisterInventory(tplayer)
|
val inventoryTasks = UnregisterInventory(tplayer)
|
||||||
TaskResolver.GiveTask(UnregisterObjectTask(tplayer).task, holsterTasks ++ lockerTask ++ inventoryTasks)
|
TaskResolver.GiveTask(UnregisterObjectTask(tplayer).task, holsterTasks ++ lockerTask ++ inventoryTasks)
|
||||||
}
|
}
|
||||||
|
|
@ -392,7 +392,7 @@ object GUIDTask {
|
||||||
if (!iter.hasNext) {
|
if (!iter.hasNext) {
|
||||||
list
|
list
|
||||||
} else {
|
} else {
|
||||||
iter.next.Equipment match {
|
iter.next().Equipment match {
|
||||||
case Some(item) =>
|
case Some(item) =>
|
||||||
recursiveVisibleSlotTaskBuilding(iter, func, list :+ func(item))
|
recursiveVisibleSlotTaskBuilding(iter, func, list :+ func(item))
|
||||||
case None =>
|
case None =>
|
||||||
|
|
|
||||||
|
|
@ -76,7 +76,7 @@ class TaskResolver() extends Actor {
|
||||||
TimeoutCleanup()
|
TimeoutCleanup()
|
||||||
|
|
||||||
case msg =>
|
case msg =>
|
||||||
log.warn(s"$self received an unexpected message $msg from $sender")
|
log.warn(s"$self received an unexpected message $msg from ${sender()}")
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -408,7 +408,7 @@ object TaskResolver {
|
||||||
if (!iter.hasNext) {
|
if (!iter.hasNext) {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
val index: Int = iter.next
|
val index: Int = iter.next()
|
||||||
if (tasks(index).task.isComplete == resolution) {
|
if (tasks(index).task.isComplete == resolution) {
|
||||||
Some(index)
|
Some(index)
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -428,7 +428,7 @@ object TaskResolver {
|
||||||
if (!iter.hasNext) {
|
if (!iter.hasNext) {
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
if (iter.next.isComplete == resolution) {
|
if (iter.next().isComplete == resolution) {
|
||||||
filterCompletionMatch(iter, resolution)
|
filterCompletionMatch(iter, resolution)
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
|
|
@ -452,7 +452,7 @@ object TaskResolver {
|
||||||
if (!iter.hasNext) {
|
if (!iter.hasNext) {
|
||||||
indexList
|
indexList
|
||||||
} else {
|
} else {
|
||||||
val index: Int = iter.next
|
val index: Int = iter.next()
|
||||||
val taskEntry = tasks(index)
|
val taskEntry = tasks(index)
|
||||||
if (
|
if (
|
||||||
taskEntry.Executing && taskEntry.task.isComplete == Task.Resolution.Incomplete && now - taskEntry.Start > taskEntry.task.Timeout
|
taskEntry.Executing && taskEntry.task.isComplete == Task.Resolution.Incomplete && now - taskEntry.Start > taskEntry.task.Timeout
|
||||||
|
|
@ -476,7 +476,7 @@ object TaskResolver {
|
||||||
if (!iter.hasNext) {
|
if (!iter.hasNext) {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
if (iter.next.task == target) {
|
if (iter.next().task == target) {
|
||||||
Some(index)
|
Some(index)
|
||||||
} else {
|
} else {
|
||||||
findTask(iter, target, index + 1)
|
findTask(iter, target, index + 1)
|
||||||
|
|
@ -496,7 +496,7 @@ object TaskResolver {
|
||||||
if (!iter.hasNext) {
|
if (!iter.hasNext) {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
val tEntry = iter.next
|
val tEntry = iter.next()
|
||||||
if (tEntry.subtasks.contains(target)) {
|
if (tEntry.subtasks.contains(target)) {
|
||||||
Some(index)
|
Some(index)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ class NumberPoolActor(pool: NumberPool) extends Actor {
|
||||||
|
|
||||||
def receive: Receive = {
|
def receive: Receive = {
|
||||||
case NumberPoolActor.GetAnyNumber(id) =>
|
case NumberPoolActor.GetAnyNumber(id) =>
|
||||||
sender ! (pool.Get() match {
|
sender() ! (pool.Get() match {
|
||||||
case Success(value) =>
|
case Success(value) =>
|
||||||
NumberPoolActor.GiveNumber(value, id)
|
NumberPoolActor.GiveNumber(value, id)
|
||||||
case Failure(ex) => ;
|
case Failure(ex) => ;
|
||||||
|
|
@ -30,7 +30,7 @@ class NumberPoolActor(pool: NumberPool) extends Actor {
|
||||||
})
|
})
|
||||||
|
|
||||||
case NumberPoolActor.GetSpecificNumber(number, id) =>
|
case NumberPoolActor.GetSpecificNumber(number, id) =>
|
||||||
sender ! (NumberPoolActor.GetSpecificNumber(pool, number) match {
|
sender() ! (NumberPoolActor.GetSpecificNumber(pool, number) match {
|
||||||
case Success(value) =>
|
case Success(value) =>
|
||||||
NumberPoolActor.GiveNumber(value, id)
|
NumberPoolActor.GiveNumber(value, id)
|
||||||
case Failure(ex) => ;
|
case Failure(ex) => ;
|
||||||
|
|
@ -41,7 +41,7 @@ class NumberPoolActor(pool: NumberPool) extends Actor {
|
||||||
val result = pool.Return(number)
|
val result = pool.Return(number)
|
||||||
val ex: Option[Throwable] = if (!result) { Some(new Exception("number was not returned")) }
|
val ex: Option[Throwable] = if (!result) { Some(new Exception("number was not returned")) }
|
||||||
else { None }
|
else { None }
|
||||||
sender ! NumberPoolActor.ReturnNumberResult(number, ex, id)
|
sender() ! NumberPoolActor.ReturnNumberResult(number, ex, id)
|
||||||
|
|
||||||
case msg =>
|
case msg =>
|
||||||
log.info(s"received an unexpected message - ${msg.toString}")
|
log.info(s"received an unexpected message - ${msg.toString}")
|
||||||
|
|
|
||||||
|
|
@ -126,11 +126,11 @@ class UniqueNumberSystem(private val guid: NumberPoolHub, private val poolActors
|
||||||
case Some(entry) =>
|
case Some(entry) =>
|
||||||
entry.replyTo ! Failure(new Exception(s"for ${entry.target} with number $number, ${ex.getMessage}"))
|
entry.replyTo ! Failure(new Exception(s"for ${entry.target} with number $number, ${ex.getMessage}"))
|
||||||
case None => ;
|
case None => ;
|
||||||
log.error(s"could not find original request $nid that caused error $ex, but pool was $sender")
|
log.error(s"could not find original request $nid that caused error $ex, but pool was ${sender()}")
|
||||||
//no callback is possible
|
//no callback is possible
|
||||||
}
|
}
|
||||||
case _ => ;
|
case _ => ;
|
||||||
log.error(s"could not find original request $id that caused error $ex, but pool was $sender")
|
log.error(s"could not find original request $id that caused error $ex, but pool was ${sender()}")
|
||||||
//no callback is possible
|
//no callback is possible
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -287,7 +287,7 @@ class GridInventory extends Container {
|
||||||
if (!cells.hasNext) {
|
if (!cells.hasNext) {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
val index = cells.next + offset
|
val index = cells.next() + offset
|
||||||
CheckCollisionsAsGrid(index, tWidth, tHeight) match {
|
CheckCollisionsAsGrid(index, tWidth, tHeight) match {
|
||||||
case Success(Nil) =>
|
case Success(Nil) =>
|
||||||
Some(index)
|
Some(index)
|
||||||
|
|
@ -466,7 +466,7 @@ class GridInventory extends Container {
|
||||||
if (!iter.hasNext) {
|
if (!iter.hasNext) {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
val index = iter.next
|
val index = iter.next()
|
||||||
if (items(index).obj.GUID == guid) {
|
if (items(index).obj.GUID == guid) {
|
||||||
Some(index)
|
Some(index)
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -567,7 +567,7 @@ class GridInventory extends Container {
|
||||||
updated: List[List[Int]]
|
updated: List[List[Int]]
|
||||||
): List[List[Int]] = {
|
): List[List[Int]] = {
|
||||||
if (original.hasNext) {
|
if (original.hasNext) {
|
||||||
val target = original.next
|
val target = original.next()
|
||||||
val filtered = updated.filterNot(item => item.equals(target))
|
val filtered = updated.filterNot(item => item.equals(target))
|
||||||
val newupdated = if (filtered.size == updated.size) {
|
val newupdated = if (filtered.size == updated.size) {
|
||||||
updated //the lists are the same size, nothing was filtered
|
updated //the lists are the same size, nothing was filtered
|
||||||
|
|
@ -588,7 +588,7 @@ class GridInventory extends Container {
|
||||||
*/
|
*/
|
||||||
def Clear(): List[InventoryItem] = {
|
def Clear(): List[InventoryItem] = {
|
||||||
val list = items.values.toList
|
val list = items.values.toList
|
||||||
items.clear
|
items.clear()
|
||||||
entryIndex.set(0)
|
entryIndex.set(0)
|
||||||
grid = SetCellsOnlyNoOffset(0, width, height)
|
grid = SetCellsOnlyNoOffset(0, width, height)
|
||||||
list
|
list
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
// Copyright (c) 2017 PSForever
|
// Copyright (c) 2017 PSForever
|
||||||
package net.psforever.objects.loadouts
|
package net.psforever.objects.loadouts
|
||||||
|
|
||||||
import net.psforever.types.{CertificationType, ExoSuitType}
|
import net.psforever.objects.avatar.Certification
|
||||||
|
import net.psforever.types.ExoSuitType
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A blueprint of a player's uniform, their holster items, and their inventory items, saved in a specific state.
|
* A blueprint of a player's uniform, their holster items, and their inventory items, saved in a specific state.
|
||||||
|
|
@ -12,6 +13,7 @@ import net.psforever.types.{CertificationType, ExoSuitType}
|
||||||
* The ten-long list is initialized with `FavoritesMessage` packets assigned to the "Infantry" list.
|
* The ten-long list is initialized with `FavoritesMessage` packets assigned to the "Infantry" list.
|
||||||
* Specific entries are added or removed using `FavoritesRequest` packets,
|
* Specific entries are added or removed using `FavoritesRequest` packets,
|
||||||
* re-established using other conventional game packets.
|
* re-established using other conventional game packets.
|
||||||
|
*
|
||||||
* @param label the name by which this inventory will be known when displayed in a Favorites list;
|
* @param label the name by which this inventory will be known when displayed in a Favorites list;
|
||||||
* field gets inherited
|
* field gets inherited
|
||||||
* @param visible_slots simplified representation of the `Equipment` that can see "seen" on the target;
|
* @param visible_slots simplified representation of the `Equipment` that can see "seen" on the target;
|
||||||
|
|
@ -106,15 +108,16 @@ object InfantryLoadout {
|
||||||
* Assuming the exo-suit is a mechanized assault type,
|
* Assuming the exo-suit is a mechanized assault type,
|
||||||
* use the subtype to determine what certifications would be valid for permitted access to that specific exo-suit.
|
* use the subtype to determine what certifications would be valid for permitted access to that specific exo-suit.
|
||||||
* The "C" does not stand for "certification."
|
* The "C" does not stand for "certification."
|
||||||
|
*
|
||||||
* @see `CertificationType`
|
* @see `CertificationType`
|
||||||
* @param subtype the numeric subtype
|
* @param subtype the numeric subtype
|
||||||
* @return a `Set` of all certifications that would grant access to the mechanized assault exo-suit subtype
|
* @return a `Set` of all certifications that would grant access to the mechanized assault exo-suit subtype
|
||||||
*/
|
*/
|
||||||
def DetermineSubtypeC(subtype: Int): Set[CertificationType.Value] =
|
def DetermineSubtypeC(subtype: Int): Set[Certification] =
|
||||||
subtype match {
|
subtype match {
|
||||||
case 1 => Set(CertificationType.AIMAX, CertificationType.UniMAX)
|
case 1 => Set(Certification.AIMAX, Certification.UniMAX)
|
||||||
case 2 => Set(CertificationType.AVMAX, CertificationType.UniMAX)
|
case 2 => Set(Certification.AVMAX, Certification.UniMAX)
|
||||||
case 3 => Set(CertificationType.AAMAX, CertificationType.UniMAX)
|
case 3 => Set(Certification.AAMAX, Certification.UniMAX)
|
||||||
case _ => Set.empty[CertificationType.Value]
|
case _ => Set.empty[Certification]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -203,7 +203,7 @@ object Loadout {
|
||||||
if (!iter.hasNext) {
|
if (!iter.hasNext) {
|
||||||
list
|
list
|
||||||
} else {
|
} else {
|
||||||
val entry = iter.next
|
val entry = iter.next()
|
||||||
entry.Equipment match {
|
entry.Equipment match {
|
||||||
case Some(obj) =>
|
case Some(obj) =>
|
||||||
recursiveHolsterSimplifications(iter, index + 1, list :+ SimplifiedEntry(buildSimplification(obj), index))
|
recursiveHolsterSimplifications(iter, index + 1, list :+ SimplifiedEntry(buildSimplification(obj), index))
|
||||||
|
|
@ -231,7 +231,7 @@ object Loadout {
|
||||||
if (!iter.hasNext) {
|
if (!iter.hasNext) {
|
||||||
list
|
list
|
||||||
} else {
|
} else {
|
||||||
val entry = iter.next
|
val entry = iter.next()
|
||||||
val fmodeSimp = if (entry.Box.AmmoType == entry.AmmoType) {
|
val fmodeSimp = if (entry.Box.AmmoType == entry.AmmoType) {
|
||||||
ShorthandAmmoSlot(
|
ShorthandAmmoSlot(
|
||||||
entry.AmmoTypeIndex,
|
entry.AmmoTypeIndex,
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
// Copyright (c) 2019 PSForever
|
// Copyright (c) 2019 PSForever
|
||||||
package net.psforever.objects.loadouts
|
package net.psforever.objects.loadouts
|
||||||
|
|
||||||
import net.psforever.types.CertificationType
|
import net.psforever.objects.avatar.Certification
|
||||||
|
|
||||||
final case class SquadPositionLoadout(
|
final case class SquadPositionLoadout(
|
||||||
index: Int,
|
index: Int,
|
||||||
role: String,
|
role: String,
|
||||||
orders: String,
|
orders: String,
|
||||||
requirements: Set[CertificationType.Value]
|
requirements: Set[Certification]
|
||||||
)
|
)
|
||||||
|
|
||||||
final case class SquadLoadout(task: String, zone_id: Option[Int], members: List[SquadPositionLoadout])
|
final case class SquadLoadout(task: String, zone_id: Option[Int], members: List[SquadPositionLoadout])
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ object FactionAffinityBehavior {
|
||||||
val convertBehavior: Receive = {
|
val convertBehavior: Receive = {
|
||||||
case FactionAffinity.ConvertFactionAffinity(faction) =>
|
case FactionAffinity.ConvertFactionAffinity(faction) =>
|
||||||
FactionObject.Faction = faction
|
FactionObject.Faction = faction
|
||||||
sender ! FactionAffinity.AssertFactionAffinity(FactionObject, faction)
|
sender() ! FactionAffinity.AssertFactionAffinity(FactionObject, faction)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -36,7 +36,7 @@ object FactionAffinityBehavior {
|
||||||
|
|
||||||
val checkBehavior: Receive = {
|
val checkBehavior: Receive = {
|
||||||
case FactionAffinity.ConfirmFactionAffinity() | FactionAffinity.AssertFactionAffinity(_, _) =>
|
case FactionAffinity.ConfirmFactionAffinity() | FactionAffinity.AssertFactionAffinity(_, _) =>
|
||||||
sender ! FactionAffinity.AssertFactionAffinity(FactionObject, FactionObject.Faction)
|
sender() ! FactionAffinity.AssertFactionAffinity(FactionObject, FactionObject.Faction)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -59,36 +59,36 @@ trait ContainableBehavior {
|
||||||
|
|
||||||
case msg: ContainableMsg if waitOnMoveItemOps == 2 =>
|
case msg: ContainableMsg if waitOnMoveItemOps == 2 =>
|
||||||
//all standard messages are blocked
|
//all standard messages are blocked
|
||||||
RepeatMessageLater(ContainableBehavior.Defer(msg, sender))
|
RepeatMessageLater(ContainableBehavior.Defer(msg, sender()))
|
||||||
MessageDeferredCallback(msg)
|
MessageDeferredCallback(msg)
|
||||||
|
|
||||||
case msg: DeferrableMsg if waitOnMoveItemOps == 1 =>
|
case msg: DeferrableMsg if waitOnMoveItemOps == 1 =>
|
||||||
//insertion messages not related to an item move attempt are blocked
|
//insertion messages not related to an item move attempt are blocked
|
||||||
RepeatMessageLater(ContainableBehavior.Defer(msg, sender))
|
RepeatMessageLater(ContainableBehavior.Defer(msg, sender()))
|
||||||
MessageDeferredCallback(msg)
|
MessageDeferredCallback(msg)
|
||||||
|
|
||||||
/* normal messages */
|
/* normal messages */
|
||||||
case Containable.RemoveItemFromSlot(None, Some(slot)) =>
|
case Containable.RemoveItemFromSlot(None, Some(slot)) =>
|
||||||
sender ! LocalRemoveItemFromSlot(slot)
|
sender() ! LocalRemoveItemFromSlot(slot)
|
||||||
|
|
||||||
case Containable.RemoveItemFromSlot(Some(item), _) =>
|
case Containable.RemoveItemFromSlot(Some(item), _) =>
|
||||||
sender ! LocalRemoveItemFromSlot(item)
|
sender() ! LocalRemoveItemFromSlot(item)
|
||||||
|
|
||||||
case Containable.PutItemInSlot(item, dest) =>
|
case Containable.PutItemInSlot(item, dest) =>
|
||||||
/* can be deferred */
|
/* can be deferred */
|
||||||
sender ! LocalPutItemInSlot(item, dest)
|
sender() ! LocalPutItemInSlot(item, dest)
|
||||||
|
|
||||||
case Containable.PutItemInSlotOnly(item, dest) =>
|
case Containable.PutItemInSlotOnly(item, dest) =>
|
||||||
/* can be deferred */
|
/* can be deferred */
|
||||||
sender ! LocalPutItemInSlotOnly(item, dest)
|
sender() ! LocalPutItemInSlotOnly(item, dest)
|
||||||
|
|
||||||
case Containable.PutItemAway(item) =>
|
case Containable.PutItemAway(item) =>
|
||||||
/* can be deferred */
|
/* can be deferred */
|
||||||
sender ! LocalPutItemAway(item)
|
sender() ! LocalPutItemAway(item)
|
||||||
|
|
||||||
case Containable.PutItemInSlotOrAway(item, dest) =>
|
case Containable.PutItemInSlotOrAway(item, dest) =>
|
||||||
/* can be deferred */
|
/* can be deferred */
|
||||||
sender ! LocalPutItemInSlotOrAway(item, dest)
|
sender() ! LocalPutItemInSlotOrAway(item, dest)
|
||||||
|
|
||||||
case msg @ Containable.MoveItem(destination, equipment, destSlot) =>
|
case msg @ Containable.MoveItem(destination, equipment, destSlot) =>
|
||||||
/* can be deferred */
|
/* can be deferred */
|
||||||
|
|
@ -146,10 +146,10 @@ trait ContainableBehavior {
|
||||||
}
|
}
|
||||||
|
|
||||||
case ContainableBehavior.MoveItemPutItemInSlot(item, dest) =>
|
case ContainableBehavior.MoveItemPutItemInSlot(item, dest) =>
|
||||||
sender ! LocalPutItemInSlot(item, dest)
|
sender() ! LocalPutItemInSlot(item, dest)
|
||||||
|
|
||||||
case ContainableBehavior.MoveItemPutItemInSlotOrAway(item, dest) =>
|
case ContainableBehavior.MoveItemPutItemInSlotOrAway(item, dest) =>
|
||||||
sender ! LocalPutItemInSlotOrAway(item, dest)
|
sender() ! LocalPutItemInSlotOrAway(item, dest)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Functions (message control) */
|
/* Functions (message control) */
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ object DamageableAmenity {
|
||||||
*/
|
*/
|
||||||
def DestructionAwareness(target: Damageable.Target, cause: ResolvedProjectile): Unit = {
|
def DestructionAwareness(target: Damageable.Target, cause: ResolvedProjectile): Unit = {
|
||||||
val zone = target.Zone
|
val zone = target.Zone
|
||||||
val zoneId = zone.Id
|
val zoneId = zone.id
|
||||||
val events = zone.AvatarEvents
|
val events = zone.AvatarEvents
|
||||||
val targetGUID = target.GUID
|
val targetGUID = target.GUID
|
||||||
events ! AvatarServiceMessage(zoneId, AvatarAction.PlanetsideAttributeToAll(targetGUID, 50, 1))
|
events ! AvatarServiceMessage(zoneId, AvatarAction.PlanetsideAttributeToAll(targetGUID, 50, 1))
|
||||||
|
|
|
||||||
|
|
@ -167,7 +167,7 @@ object DamageableEntity {
|
||||||
if (!target.Destroyed) {
|
if (!target.Destroyed) {
|
||||||
val tguid = target.GUID
|
val tguid = target.GUID
|
||||||
zone.AvatarEvents ! AvatarServiceMessage(
|
zone.AvatarEvents ! AvatarServiceMessage(
|
||||||
zone.Id,
|
zone.id,
|
||||||
AvatarAction.PlanetsideAttributeToAll(tguid, 0, target.Health)
|
AvatarAction.PlanetsideAttributeToAll(tguid, 0, target.Health)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -195,7 +195,7 @@ object DamageableEntity {
|
||||||
target.Actor ! JammableUnit.ClearJammeredStatus()
|
target.Actor ! JammableUnit.ClearJammeredStatus()
|
||||||
//
|
//
|
||||||
val zone = target.Zone
|
val zone = target.Zone
|
||||||
val zoneId = zone.Id
|
val zoneId = zone.id
|
||||||
val tguid = target.GUID
|
val tguid = target.GUID
|
||||||
val attribution = target.Zone.LivePlayers.find { p => cause.projectile.owner.Name.equals(p.Name) } match {
|
val attribution = target.Zone.LivePlayers.find { p => cause.projectile.owner.Name.equals(p.Name) } match {
|
||||||
case Some(player) => player.GUID
|
case Some(player) => player.GUID
|
||||||
|
|
|
||||||
|
|
@ -180,7 +180,7 @@ object DamageableVehicle {
|
||||||
if (target.Shields > 0) {
|
if (target.Shields > 0) {
|
||||||
target.Shields = 0
|
target.Shields = 0
|
||||||
zone.VehicleEvents ! VehicleServiceMessage(
|
zone.VehicleEvents ! VehicleServiceMessage(
|
||||||
zone.Id,
|
zone.id,
|
||||||
VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, target.GUID, 68, 0)
|
VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, target.GUID, 68, 0)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,7 @@ object DamageableWeaponTurret {
|
||||||
def DestructionAwareness(target: Damageable.Target with MountedWeapons, cause: ResolvedProjectile): Unit = {
|
def DestructionAwareness(target: Damageable.Target with MountedWeapons, cause: ResolvedProjectile): Unit = {
|
||||||
//wreckage has no (visible) mounted weapons
|
//wreckage has no (visible) mounted weapons
|
||||||
val zone = target.Zone
|
val zone = target.Zone
|
||||||
val zoneId = zone.Id
|
val zoneId = zone.id
|
||||||
val avatarEvents = zone.AvatarEvents
|
val avatarEvents = zone.AvatarEvents
|
||||||
target.Weapons.values
|
target.Weapons.values
|
||||||
.filter {
|
.filter {
|
||||||
|
|
|
||||||
|
|
@ -25,114 +25,133 @@ trait DeploymentBehavior {
|
||||||
|
|
||||||
val deployBehavior: Receive = {
|
val deployBehavior: Receive = {
|
||||||
case Deployment.TryDeploymentChange(state) =>
|
case Deployment.TryDeploymentChange(state) =>
|
||||||
sender ! TryDeploymentStateChange(state)
|
sender() ! TryDeploymentStateChange(state)
|
||||||
|
|
||||||
case Deployment.TryDeploy(state) =>
|
case Deployment.TryDeploy(state) =>
|
||||||
sender ! TryDeployStateChange(state)
|
sender() ! TryDeployStateChange(state)
|
||||||
|
|
||||||
case Deployment.TryUndeploy(state) =>
|
case Deployment.TryUndeploy(state) =>
|
||||||
sender ! TryUndeployStateChange(state)
|
sender() ! TryUndeployStateChange(state)
|
||||||
}
|
}
|
||||||
|
|
||||||
def TryDeploymentStateChange(state : DriveState.Value) : Any = {
|
def TryDeploymentStateChange(state: DriveState.Value): Any = {
|
||||||
val obj = DeploymentObject
|
val obj = DeploymentObject
|
||||||
val prevState = obj.DeploymentState
|
val prevState = obj.DeploymentState
|
||||||
if(TryDeploymentChange(obj, state)) {
|
if (TryDeploymentChange(obj, state)) {
|
||||||
if(Deployment.CheckForDeployState(state)) {
|
if (Deployment.CheckForDeployState(state)) {
|
||||||
DeploymentAction(obj, state, prevState)
|
DeploymentAction(obj, state, prevState)
|
||||||
Deployment.CanDeploy(obj, state)
|
Deployment.CanDeploy(obj, state)
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
UndeploymentAction(obj, state, prevState)
|
UndeploymentAction(obj, state, prevState)
|
||||||
Deployment.CanUndeploy(obj, state)
|
Deployment.CanUndeploy(obj, state)
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
Deployment.CanNotChangeDeployment(obj, state, "incorrect transition state")
|
Deployment.CanNotChangeDeployment(obj, state, "incorrect transition state")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def TryDeployStateChange(state : DriveState.Value) : Any = {
|
def TryDeployStateChange(state: DriveState.Value): Any = {
|
||||||
val obj = DeploymentObject
|
val obj = DeploymentObject
|
||||||
val prevState = obj.DeploymentState
|
val prevState = obj.DeploymentState
|
||||||
if(Deployment.CheckForDeployState(state) && TryDeploymentChange(obj, state)) {
|
if (Deployment.CheckForDeployState(state) && TryDeploymentChange(obj, state)) {
|
||||||
DeploymentAction(obj, state, prevState)
|
DeploymentAction(obj, state, prevState)
|
||||||
Deployment.CanDeploy(obj, state)
|
Deployment.CanDeploy(obj, state)
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
Deployment.CanNotChangeDeployment(obj, state, "incorrect deploy transition state")
|
Deployment.CanNotChangeDeployment(obj, state, "incorrect deploy transition state")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def TryUndeployStateChange(state : DriveState.Value) : Any = {
|
def TryUndeployStateChange(state: DriveState.Value): Any = {
|
||||||
val obj = DeploymentObject
|
val obj = DeploymentObject
|
||||||
val prevState = obj.DeploymentState
|
val prevState = obj.DeploymentState
|
||||||
if(Deployment.CheckForUndeployState(state) && TryUndeploymentChange(obj, state)) {
|
if (Deployment.CheckForUndeployState(state) && TryUndeploymentChange(obj, state)) {
|
||||||
UndeploymentAction(obj, state, prevState)
|
UndeploymentAction(obj, state, prevState)
|
||||||
Deployment.CanUndeploy(obj, state)
|
Deployment.CanUndeploy(obj, state)
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
Deployment.CanNotChangeDeployment(obj, state, "incorrect undeploy transition state")
|
Deployment.CanNotChangeDeployment(obj, state, "incorrect undeploy transition state")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def TryDeploymentChange(obj : Deployment.DeploymentObject, state : DriveState.Value) : Boolean = {
|
def TryDeploymentChange(obj: Deployment.DeploymentObject, state: DriveState.Value): Boolean = {
|
||||||
DeploymentBehavior.TryDeploymentChange(obj, state)
|
DeploymentBehavior.TryDeploymentChange(obj, state)
|
||||||
}
|
}
|
||||||
|
|
||||||
def TryUndeploymentChange(obj : Deployment.DeploymentObject, state : DriveState.Value) : Boolean = {
|
def TryUndeploymentChange(obj: Deployment.DeploymentObject, state: DriveState.Value): Boolean = {
|
||||||
DeploymentBehavior.TryDeploymentChange(obj, state)
|
DeploymentBehavior.TryDeploymentChange(obj, state)
|
||||||
}
|
}
|
||||||
|
|
||||||
def DeploymentAction(obj : Deployment.DeploymentObject, state : DriveState.Value, prevState : DriveState.Value) : DriveState.Value = {
|
def DeploymentAction(
|
||||||
val guid = obj.GUID
|
obj: Deployment.DeploymentObject,
|
||||||
val zone = obj.Zone
|
state: DriveState.Value,
|
||||||
val zoneChannel = zone.Id
|
prevState: DriveState.Value
|
||||||
val GUID0 = Service.defaultPlayerGUID
|
): DriveState.Value = {
|
||||||
|
val guid = obj.GUID
|
||||||
|
val zone = obj.Zone
|
||||||
|
val zoneChannel = zone.id
|
||||||
|
val GUID0 = Service.defaultPlayerGUID
|
||||||
//TODO remove this arbitrary allowance angle when no longer helpful
|
//TODO remove this arbitrary allowance angle when no longer helpful
|
||||||
if(obj.Orientation.x > 30 && obj.Orientation.x < 330) {
|
if (obj.Orientation.x > 30 && obj.Orientation.x < 330) {
|
||||||
obj.DeploymentState = prevState
|
obj.DeploymentState = prevState
|
||||||
prevState
|
prevState
|
||||||
}
|
} else if (state == DriveState.Deploying) {
|
||||||
else if(state == DriveState.Deploying) {
|
|
||||||
obj.Velocity = Some(Vector3.Zero) //no velocity
|
obj.Velocity = Some(Vector3.Zero) //no velocity
|
||||||
zone.VehicleEvents ! VehicleServiceMessage(zoneChannel, VehicleAction.DeployRequest(GUID0, guid, state, 0, false, Vector3.Zero))
|
zone.VehicleEvents ! VehicleServiceMessage(
|
||||||
context.system.scheduler.scheduleOnce(obj.DeployTime milliseconds, obj.Actor, Deployment.TryDeploy(DriveState.Deployed))
|
zoneChannel,
|
||||||
|
VehicleAction.DeployRequest(GUID0, guid, state, 0, false, Vector3.Zero)
|
||||||
|
)
|
||||||
|
context.system.scheduler.scheduleOnce(
|
||||||
|
obj.DeployTime milliseconds,
|
||||||
|
obj.Actor,
|
||||||
|
Deployment.TryDeploy(DriveState.Deployed)
|
||||||
|
)
|
||||||
state
|
state
|
||||||
}
|
} else if (state == DriveState.Deployed) {
|
||||||
else if(state == DriveState.Deployed) {
|
|
||||||
obj.Velocity = Some(Vector3.Zero) //no velocity
|
obj.Velocity = Some(Vector3.Zero) //no velocity
|
||||||
zone.VehicleEvents ! VehicleServiceMessage(zoneChannel, VehicleAction.DeployRequest(GUID0, guid, state, 0, false, Vector3.Zero))
|
zone.VehicleEvents ! VehicleServiceMessage(
|
||||||
|
zoneChannel,
|
||||||
|
VehicleAction.DeployRequest(GUID0, guid, state, 0, false, Vector3.Zero)
|
||||||
|
)
|
||||||
state
|
state
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
prevState
|
prevState
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def UndeploymentAction(obj : Deployment.DeploymentObject, state : DriveState.Value, prevState : DriveState.Value) : DriveState.Value = {
|
def UndeploymentAction(
|
||||||
val guid = obj.GUID
|
obj: Deployment.DeploymentObject,
|
||||||
val zone = obj.Zone
|
state: DriveState.Value,
|
||||||
val zoneChannel = zone.Id
|
prevState: DriveState.Value
|
||||||
val GUID0 = Service.defaultPlayerGUID
|
): DriveState.Value = {
|
||||||
if(state == DriveState.Undeploying) {
|
val guid = obj.GUID
|
||||||
zone.VehicleEvents ! VehicleServiceMessage(zoneChannel, VehicleAction.DeployRequest(GUID0, guid, state, 0, false, Vector3.Zero))
|
val zone = obj.Zone
|
||||||
|
val zoneChannel = zone.id
|
||||||
|
val GUID0 = Service.defaultPlayerGUID
|
||||||
|
if (state == DriveState.Undeploying) {
|
||||||
|
zone.VehicleEvents ! VehicleServiceMessage(
|
||||||
|
zoneChannel,
|
||||||
|
VehicleAction.DeployRequest(GUID0, guid, state, 0, false, Vector3.Zero)
|
||||||
|
)
|
||||||
import scala.concurrent.ExecutionContext.Implicits.global
|
import scala.concurrent.ExecutionContext.Implicits.global
|
||||||
context.system.scheduler.scheduleOnce(obj.UndeployTime milliseconds, obj.Actor, Deployment.TryUndeploy(DriveState.Mobile))
|
context.system.scheduler.scheduleOnce(
|
||||||
|
obj.UndeployTime milliseconds,
|
||||||
|
obj.Actor,
|
||||||
|
Deployment.TryUndeploy(DriveState.Mobile)
|
||||||
|
)
|
||||||
state
|
state
|
||||||
}
|
} else if (state == DriveState.Mobile) {
|
||||||
else if(state == DriveState.Mobile) {
|
zone.VehicleEvents ! VehicleServiceMessage(
|
||||||
zone.VehicleEvents ! VehicleServiceMessage(zoneChannel, VehicleAction.DeployRequest(GUID0, guid, state, 0, false, Vector3.Zero))
|
zoneChannel,
|
||||||
|
VehicleAction.DeployRequest(GUID0, guid, state, 0, false, Vector3.Zero)
|
||||||
|
)
|
||||||
state
|
state
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
prevState
|
prevState
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
object DeploymentBehavior {
|
object DeploymentBehavior {
|
||||||
def TryDeploymentChange(obj : Deployment.DeploymentObject, state : DriveState.Value) : Boolean = {
|
def TryDeploymentChange(obj: Deployment.DeploymentObject, state: DriveState.Value): Boolean = {
|
||||||
Deployment.NextState(obj.DeploymentState) == state && (obj.DeploymentState = state) == state
|
Deployment.NextState(obj.DeploymentState) == state && (obj.DeploymentState = state) == state
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,9 +14,9 @@ class DoorControl(door: Door) extends Actor with FactionAffinityBehavior.Check {
|
||||||
def receive: Receive =
|
def receive: Receive =
|
||||||
checkBehavior.orElse {
|
checkBehavior.orElse {
|
||||||
case Door.Use(player, msg) =>
|
case Door.Use(player, msg) =>
|
||||||
sender ! Door.DoorMessage(player, msg, door.Use(player, msg))
|
sender() ! Door.DoorMessage(player, msg, door.Use(player, msg))
|
||||||
|
|
||||||
case _ =>
|
case _ =>
|
||||||
sender ! Door.NoEvent()
|
sender() ! Door.NoEvent()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,7 @@ class GeneratorControl(gen: Generator)
|
||||||
GeneratorControl.UpdateOwner(gen)
|
GeneratorControl.UpdateOwner(gen)
|
||||||
//kaboom
|
//kaboom
|
||||||
zone.AvatarEvents ! AvatarServiceMessage(
|
zone.AvatarEvents ! AvatarServiceMessage(
|
||||||
zone.Id,
|
zone.id,
|
||||||
AvatarAction.SendResponse(
|
AvatarAction.SendResponse(
|
||||||
Service.defaultPlayerGUID,
|
Service.defaultPlayerGUID,
|
||||||
TriggerEffectMessage(gen.GUID, "explosion_generator", None, None)
|
TriggerEffectMessage(gen.GUID, "explosion_generator", None, None)
|
||||||
|
|
|
||||||
|
|
@ -21,19 +21,19 @@ object GenericHackables {
|
||||||
* @return the percentage amount of progress per tick
|
* @return the percentage amount of progress per tick
|
||||||
*/
|
*/
|
||||||
def GetHackSpeed(player: Player, obj: PlanetSideServerObject): Float = {
|
def GetHackSpeed(player: Player, obj: PlanetSideServerObject): Float = {
|
||||||
val playerHackLevel = Player.GetHackLevel(player)
|
val playerHackLevel = player.avatar.hackingSkillLevel()
|
||||||
val timeToHack = obj match {
|
val timeToHack = obj match {
|
||||||
case vehicle: Vehicle => vehicle.JackingDuration(playerHackLevel).toFloat
|
case vehicle: Vehicle => vehicle.JackingDuration(playerHackLevel).toFloat
|
||||||
case hackable: Hackable => hackable.HackDuration(playerHackLevel).toFloat
|
case hackable: Hackable => hackable.HackDuration(playerHackLevel).toFloat
|
||||||
case _ =>
|
case _ =>
|
||||||
log.warn(
|
log.warn(
|
||||||
s"${player.Name} tried to hack an object that has no hack time defined - ${obj.Definition.Name}#${obj.GUID} on ${obj.Zone.Id}"
|
s"${player.Name} tried to hack an object that has no hack time defined - ${obj.Definition.Name}#${obj.GUID} on ${obj.Zone.id}"
|
||||||
)
|
)
|
||||||
0f
|
0f
|
||||||
}
|
}
|
||||||
if (timeToHack == 0) {
|
if (timeToHack == 0) {
|
||||||
log.warn(
|
log.warn(
|
||||||
s"${player.Name} tried to hack an object that they don't have the correct hacking level for - ${obj.Definition.Name}#${obj.GUID} on ${obj.Zone.Id}"
|
s"${player.Name} tried to hack an object that they don't have the correct hacking level for - ${obj.Definition.Name}#${obj.GUID} on ${obj.Zone.id}"
|
||||||
)
|
)
|
||||||
0f
|
0f
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -110,7 +110,7 @@ object GenericHackables {
|
||||||
ask(target.Actor, CommonMessages.Hack(tplayer, target))(1 second).mapTo[Boolean].onComplete {
|
ask(target.Actor, CommonMessages.Hack(tplayer, target))(1 second).mapTo[Boolean].onComplete {
|
||||||
case Success(_) =>
|
case Success(_) =>
|
||||||
val zone = target.Zone
|
val zone = target.Zone
|
||||||
val zoneId = zone.Id
|
val zoneId = zone.id
|
||||||
val pguid = tplayer.GUID
|
val pguid = tplayer.GUID
|
||||||
zone.LocalEvents ! LocalServiceMessage(
|
zone.LocalEvents ! LocalServiceMessage(
|
||||||
zoneId,
|
zoneId,
|
||||||
|
|
@ -118,7 +118,8 @@ object GenericHackables {
|
||||||
)
|
)
|
||||||
zone.LocalEvents ! LocalServiceMessage(
|
zone.LocalEvents ! LocalServiceMessage(
|
||||||
zoneId,
|
zoneId,
|
||||||
LocalAction.HackTemporarily(pguid, zone, target, unk, target.HackEffectDuration(Player.GetHackLevel(user)))
|
LocalAction
|
||||||
|
.HackTemporarily(pguid, zone, target, unk, target.HackEffectDuration(user.avatar.hackingSkillLevel()))
|
||||||
)
|
)
|
||||||
case Failure(_) => log.warn(s"Hack message failed on target guid: ${target.GUID}")
|
case Failure(_) => log.warn(s"Hack message failed on target guid: ${target.GUID}")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ object HackableBehavior {
|
||||||
case CommonMessages.Hack(player, _, _) =>
|
case CommonMessages.Hack(player, _, _) =>
|
||||||
val obj = HackableObject
|
val obj = HackableObject
|
||||||
obj.HackedBy = player
|
obj.HackedBy = player
|
||||||
sender ! true
|
sender() ! true
|
||||||
|
|
||||||
case CommonMessages.ClearHack() =>
|
case CommonMessages.ClearHack() =>
|
||||||
val obj = HackableObject
|
val obj = HackableObject
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,7 @@ class ImplantTerminalMechControl(mech: ImplantTerminalMech)
|
||||||
//TODO setup certifications check
|
//TODO setup certifications check
|
||||||
mech.Owner match {
|
mech.Owner match {
|
||||||
case b: Building if (b.Faction != player.Faction || b.CaptureTerminalIsHacked) && mech.HackedBy.isEmpty =>
|
case b: Building if (b.Faction != player.Faction || b.CaptureTerminalIsHacked) && mech.HackedBy.isEmpty =>
|
||||||
sender ! CommonMessages.Progress(
|
sender() ! CommonMessages.Progress(
|
||||||
GenericHackables.GetHackSpeed(player, mech),
|
GenericHackables.GetHackSpeed(player, mech),
|
||||||
GenericHackables.FinishHacking(mech, player, 3212836864L),
|
GenericHackables.FinishHacking(mech, player, 3212836864L),
|
||||||
GenericHackables.HackingTickAction(progressType = 1, player, mech, item.GUID)
|
GenericHackables.HackingTickAction(progressType = 1, player, mech, item.GUID)
|
||||||
|
|
@ -60,7 +60,7 @@ class ImplantTerminalMechControl(mech: ImplantTerminalMech)
|
||||||
player: Player
|
player: Player
|
||||||
): Boolean = {
|
): Boolean = {
|
||||||
val zone = obj.Zone
|
val zone = obj.Zone
|
||||||
zone.map.TerminalToInterface.get(obj.GUID.guid) match {
|
zone.map.terminalToInterface.get(obj.GUID.guid) match {
|
||||||
case Some(interface_guid) =>
|
case Some(interface_guid) =>
|
||||||
(zone.GUID(interface_guid) match {
|
(zone.GUID(interface_guid) match {
|
||||||
case Some(interface) => !interface.Destroyed
|
case Some(interface) => !interface.Destroyed
|
||||||
|
|
|
||||||
|
|
@ -26,13 +26,13 @@ class IFFLockControl(lock: IFFLock)
|
||||||
case CommonMessages.Use(player, Some(item: SimpleItem))
|
case CommonMessages.Use(player, Some(item: SimpleItem))
|
||||||
if item.Definition == GlobalDefinitions.remote_electronics_kit =>
|
if item.Definition == GlobalDefinitions.remote_electronics_kit =>
|
||||||
if (lock.Faction != player.Faction && lock.HackedBy.isEmpty) {
|
if (lock.Faction != player.Faction && lock.HackedBy.isEmpty) {
|
||||||
sender ! CommonMessages.Progress(
|
sender() ! CommonMessages.Progress(
|
||||||
GenericHackables.GetHackSpeed(player, lock),
|
GenericHackables.GetHackSpeed(player, lock),
|
||||||
GenericHackables.FinishHacking(lock, player, 1114636288L),
|
GenericHackables.FinishHacking(lock, player, 1114636288L),
|
||||||
GenericHackables.HackingTickAction(progressType = 1, player, lock, item.GUID)
|
GenericHackables.HackingTickAction(progressType = 1, player, lock, item.GUID)
|
||||||
)
|
)
|
||||||
} else if (lock.Faction == player.Faction && lock.HackedBy.nonEmpty) {
|
} else if (lock.Faction == player.Faction && lock.HackedBy.nonEmpty) {
|
||||||
sender ! CommonMessages.Progress(
|
sender() ! CommonMessages.Progress(
|
||||||
GenericHackables.GetHackSpeed(player, lock),
|
GenericHackables.GetHackSpeed(player, lock),
|
||||||
IFFLocks.FinishResecuringIFFLock(lock),
|
IFFLocks.FinishResecuringIFFLock(lock),
|
||||||
GenericHackables.HackingTickAction(progressType = 1, player, lock, item.GUID)
|
GenericHackables.HackingTickAction(progressType = 1, player, lock, item.GUID)
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ object IFFLocks {
|
||||||
def FinishResecuringIFFLock(lock: IFFLock)(): Unit = {
|
def FinishResecuringIFFLock(lock: IFFLock)(): Unit = {
|
||||||
val zone = lock.Zone
|
val zone = lock.Zone
|
||||||
lock.Zone.LocalEvents ! LocalServiceMessage(
|
lock.Zone.LocalEvents ! LocalServiceMessage(
|
||||||
zone.Id,
|
zone.id,
|
||||||
LocalAction.ClearTemporaryHack(Service.defaultPlayerGUID, lock)
|
LocalAction.ClearTemporaryHack(Service.defaultPlayerGUID, lock)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue